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

updates to open-fcoe commit:-

From: Vasu Dev <vasu.dev@intel.com>

commit 95becb29bce2d8d94687f7c83d5f009e945be3de
	Author: Chris Leech <christopher.leech@intel.com>
	Date:   Wed Mar 17 12:02:51 2010 -0700
	fipvlan: make output able to show complete interface names up to IFNAMSIZ

This update adds auto fipvlan creations beside few minor fixes & improvements.

Signed-off-by: Vasu Dev <vasu.dev@intel.com>
---

 Makefile.am                     |   20 +
 QUICKSTART                      |    3 
 configure.ac                    |    3 
 debug/fcoedump.sh               |   67 +++-
 etc/cfg-ethx                    |    5 
 fcoe_utils.c                    |  191 -----------
 fcoe_utils.h                    |   85 -----
 fcoe_utils_version.h.in         |    6 
 fcoemon.c                       |  200 ++++++++++-
 fcoemon.h                       |    7 
 fcoemon_utils.c                 |  688 ---------------------------------------
 fcoemon_utils.h                 |  268 ---------------
 fipvlan.c                       |  683 ++++++++++++++++-----------------------
 include/fcoe_utils.h            |   85 +++++
 include/fcoe_utils_version.h.in |    6 
 include/fcoemon_utils.h         |  270 +++++++++++++++
 include/fip.h                   |   25 +
 include/list.h                  |  444 -------------------------
 include/log.h                   |   15 -
 include/rtnetlink.h             |   63 ++++
 lib/Makefile.am                 |    3 
 lib/fcoe_utils.c                |  191 +++++++++++
 lib/fip.c                       |  155 +++++++++
 lib/rtnetlink.c                 |  429 ++++++++++++++++++++++++
 lib/sa_log.c                    |  256 +++++++++++++++
 lib/sa_other.c                  |   69 ++++
 lib/sa_select.c                 |  213 ++++++++++++
 lib/sa_timer.c                  |  226 +++++++++++++
 log.c                           |   87 -----
 29 files changed, 2530 insertions(+), 2233 deletions(-)
 delete mode 100644 fcoe_utils.c
 delete mode 100644 fcoe_utils.h
 delete mode 100644 fcoe_utils_version.h.in
 delete mode 100644 fcoemon_utils.c
 delete mode 100644 fcoemon_utils.h
 create mode 100644 include/fcoe_utils.h
 create mode 100644 include/fcoe_utils_version.h.in
 create mode 100644 include/fcoemon_utils.h
 delete mode 100644 include/list.h
 delete mode 100644 include/log.h
 create mode 100644 include/rtnetlink.h
 create mode 100644 lib/Makefile.am
 create mode 100644 lib/fcoe_utils.c
 create mode 100644 lib/fip.c
 create mode 100644 lib/rtnetlink.c
 create mode 100644 lib/sa_log.c
 create mode 100644 lib/sa_other.c
 create mode 100644 lib/sa_select.c
 create mode 100644 lib/sa_timer.c
 delete mode 100644 log.c


diff --git a/Makefile.am b/Makefile.am
index e533cf7..bdd510f 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1,3 +1,6 @@
+# build libutil first
+SUBDIRS = lib .
+
 ## target programs, to be built and installed in $(prefix)/sbin
 sbin_PROGRAMS = fcoeadm fcping fipvlan fcnsq fcrls
 if WITH_DCB
@@ -12,9 +15,10 @@ AM_CFLAGS = -DSYSCONFDIR="\"${sysconfdir}\""
 
 ## rules for building fcoeadm
 ## only listed sources get packaged, so must list all headers too
-fcoeadm_SOURCES = fcoeadm_display.c fcoeadm.c fcoeadm.h fcoe_utils.c \
-fcoe_utils.h fcoe_utils_version.h include/fc_scsi.h include/fc_types.h \
+fcoeadm_SOURCES = fcoeadm_display.c fcoeadm.c fcoeadm.h \
+include/fc_scsi.h include/fc_types.h \
 include/net_types.h fcoe_clif.h
+fcoeadm_LDADD = lib/libutil.a
 
 ## fcoeadm uses HBAAPI, so get the right flags for compiling and linking
 fcoeadm_CFLAGS = $(HBAAPI_CFLAGS)
@@ -22,7 +26,8 @@ fcoeadm_LDFLAGS = $(HBAAPI_LIBS)
 
 ## rules for building fcping
 ## only listed sources get packaged, so must list all headers too
-fcping_SOURCES = fcping.c fcoe_utils.c fcoe_utils.h
+fcping_SOURCES = fcping.c
+fcping_LDADD = lib/libutil.a
 
 ## fcping uses HBAAPI, so get the right flags for compiling and linking
 fcping_CFLAGS = $(HBAAPI_CFLAGS)
@@ -30,9 +35,10 @@ 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 \
-fcoe_utils.c fcoe_utils.h fcoe_utils_version.h include/fc_scsi.h \
+fcoemon_SOURCES = fcoemon.c fcoemon.h \
+include/fc_scsi.h \
 include/fc_types.h include/net_types.h fcoe_clif.h
+fcoemon_LDADD = lib/libutil.a
 
 ## fcoemon needs headers from dcbd, get the right include path for them
 fcoemon_CFLAGS = $(DCBD_CFLAGS)
@@ -40,8 +46,8 @@ fcoemon_LDFLAGS = -lrt
 
 ## rules for building fipvlan
 ## only listed sources get packaged, so must list all headers too
-fipvlan_SOURCES = fipvlan.c fcoe_utils_version.h include/fip.h \
-log.c include/log.h include/list.h
+fipvlan_SOURCES = fipvlan.c include/fip.h
+fipvlan_LDADD = lib/libutil.a
 
 ## install configuration file in $(prefix)/etc/fcoe
 fcoe_configdir = ${sysconfdir}/fcoe
diff --git a/QUICKSTART b/QUICKSTART
index 766c068..e1d0e00 100644
--- a/QUICKSTART
+++ b/QUICKSTART
@@ -54,6 +54,9 @@ git://open-fcoe.org/openfc/fcoe-utils.git
 	      LibFCoE
 	      FCoE
 
+   Enable the block layer
+	      Block layer SG support v4
+
    Device Drivers -> Network Device Support -> Ethernet (10000 Mbit)
 	      Intel(R) 10GbE PCI Express adapters support
 	      Data Center Bridging (DCB) Support
diff --git a/configure.ac b/configure.ac
index 517b9c2..db4e55c 100644
--- a/configure.ac
+++ b/configure.ac
@@ -3,6 +3,7 @@ AM_INIT_AUTOMAKE([-Wall -Werror foreign])
 
 AC_PROG_CC
 AM_PROG_CC_C_O
+AC_PROG_RANLIB
 
 PKG_CHECK_MODULES([HBAAPI], [HBAAPI])
 AC_SUBST([HBAAPI_CFLAGS])
@@ -19,6 +20,6 @@ AS_IF([test "X$with_dcb" != Xno],
 	[PKG_CHECK_MODULES([DCBD], [dcbd])
 	AC_SUBST([DCBD_CFLAGS])])
 
-AC_CONFIG_FILES([Makefile fcoe-utils.spec fcoe_utils_version.h])
+AC_CONFIG_FILES([Makefile lib/Makefile fcoe-utils.spec include/fcoe_utils_version.h])
 AC_OUTPUT
 
diff --git a/debug/fcoedump.sh b/debug/fcoedump.sh
index 3c4bac6..44e858a 100755
--- a/debug/fcoedump.sh
+++ b/debug/fcoedump.sh
@@ -1,3 +1,4 @@
+#!/bin/bash
 ###############################################################################
 #
 # fcoe_debug: print debugging information for fcoe
@@ -17,24 +18,51 @@ else
 	PHYSDEV=$DEVICE
 fi
 
-kernel_info () {
+kernel_info()
+{
 	echo -e "\n###KERNEL INFO###"
 	uname -a
+
+	if [ -f "/proc/config.gz" ]
+	then
+		echo -e "\nzcat /proc/config.gz"
+		zcat /proc/config.gz
+	else
+		echo -e "\n/proc/config.gz does not exist."
+	fi
 }
 
-system_info () {
+system_info()
+{
 	echo -e "\n###System Info###"
-	echo -e "#lsscsi:"
+
+	echo -e "#lsscsi"
 	lsscsi
-	echo -e "#lspci:"
+
+	echo -e "#lspci"
 	lspci
 
-	echo -e "#check symbols:"
+	echo -e "#grep dcbnl_init /proc/kallsyms"
 	grep dcbnl_init /proc/kallsyms
-	grep fcoe /proc/kallsyms
+
+	echo -e "#grep scsi_transport_fc /proc/kallsyms"
+	grep scsi_transport_fc /proc/kallsyms
+
+	echo -e "#grep libfc /proc/kallsyms"
+	grep libfc /proc/kallsyms | grep -v libfcoe
+
+	echo -e "#grep libfcoe /proc/kallsyms"
+	grep libfcoe /proc/kallsyms
+
+	echo -e "#grep fcoe /proc/kallsyms"
+	grep fcoe /proc/kallsyms | grep -v libfcoe
+
+	echo -e "#lsmod"
+	lsmod
 }
 
-adapter_info () {
+adapter_info()
+{
 	if [ $DEVICE != $PHYSDEV ]
 	then
 		echo -e "\n###Adapter INFO VLAN $DEVICE"
@@ -63,7 +91,8 @@ adapter_info () {
 	ifconfig $PHYSDEV
 }
 
-dcb_info () {
+dcb_info()
+{
 	echo -e "\n###DCB INFO"
 	echo -e "#tc config"
 	tc qdisc
@@ -99,7 +128,8 @@ dcb_info () {
 	dcbtool gp $PHYSDEV ll:0
 }
 
-fcoe_info () {
+fcoe_info()
+{
 	echo -e "\n###FCOE Info"
 	echo -e "#service fcoe status"
 	service fcoe status
@@ -111,8 +141,16 @@ fcoe_info () {
 	fcoeadm -t
 }
 
-sysfs_dump () {
-	echo -e "###SYSFS dump"
+bsg_info()
+{
+	echo -e "\n###BSG Info"
+	echo -e "#find /dev/bsg/"
+	find /dev/bsg/ 2>&1
+}
+
+sysfs_dump()
+{
+	echo -e "\n###SYSFS dump"
 	echo -e "#sysfs fc_host dump"
 	find /sys/class/fc_host/host*/ -type f -print -exec cat '{}' \;
 	echo -e "#sysfs fc_transport dump"
@@ -123,7 +161,8 @@ sysfs_dump () {
 	find /sys/class/fc_vports/*/ -type f -print -exec cat '{}' \;
 }
 
-logfile_dump() {
+logfile_dump()
+{
 	echo "###LOGFILES"
 	echo "#/var/log/messages"
 	cat /var/log/messages
@@ -132,12 +171,14 @@ logfile_dump() {
 	dmesg
 }
 
-fcoe_debug () {
+fcoe_debug()
+{
 	kernel_info
 	system_info
 	adapter_info
 	dcb_info
 	fcoe_info
+	bsg_info
 	sysfs_dump
 	logfile_dump
 }
diff --git a/etc/cfg-ethx b/etc/cfg-ethx
index 96eca59..b7274ac 100644
--- a/etc/cfg-ethx
+++ b/etc/cfg-ethx
@@ -7,3 +7,8 @@ FCOE_ENABLE="yes"
 ## Default:    yes
 # Indicate if DCB service is required at the Ethernet port
 DCB_REQUIRED="yes"
+
+## Type:	yes/no
+## Default:	yes
+# Indicate if VLAN discovery should be handled by fcoemon
+AUTO_VLAN="yes"
diff --git a/fcoe_utils.c b/fcoe_utils.c
deleted file mode 100644
index 506356d..0000000
--- a/fcoe_utils.c
+++ /dev/null
@@ -1,191 +0,0 @@
-/*
- * Copyright(c) 2010 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 "fcoe_utils.h"
-
-static int fcoe_sysfs_read(char *buf, int size, const char *path)
-{
-	FILE *fp;
-	int i, rc = -EINVAL;
-
-	fp = fopen(path, "r");
-	if (fp) {
-		if (fgets(buf, size, fp)) {
-			/*
-			 * Strip trailing newline by replacing
-			 * any '\r' or '\n' instances with '\0'.
-			 * It's not as elegant as it could be, but
-			 * we know that the symbolic name won't
-			 * have either of those characters until
-			 * the end of the line.
-			 */
-			for (i = 0; i < strlen(buf); i++) {
-				if (buf[i] == '\n' ||
-				    buf[i] == '\r') {
-					buf[i] = '\0';
-					break;
-				}
-			}
-			rc = 0;
-		}
-
-		fclose(fp);
-	}
-
-	return rc;
-}
-
-static int fcoe_check_fchost(const char *ifname, const char *dname)
-{
-	char buf[MAX_STR_LEN];
-	char path[MAX_PATH_LEN];
-	int rc = -EINVAL;
-
-	sprintf(path, "%s/%s/symbolic_name", SYSFS_FCHOST, dname);
-
-	if (!fcoe_sysfs_read(buf, MAX_STR_LEN, path))
-		rc = check_symbolic_name_for_interface(buf, ifname);
-
-	return rc;
-}
-
-enum fcoe_err fcoe_find_fchost(char *ifname, char *fchost, int len)
-{
-	int n, dname_len;
-	struct dirent **namelist;
-	int rc = ENOFCOECONN;
-
-	n = scandir(SYSFS_FCHOST, &namelist, 0, alphasort);
-
-	for (n-- ; n >= 0 ; n--) {
-		if (rc) {
-			/* check symbolic name */
-			if (!fcoe_check_fchost(ifname, namelist[n]->d_name)) {
-				dname_len = strnlen(namelist[n]->d_name, len);
-
-				if (len > dname_len) {
-					strncpy(fchost, namelist[n]->d_name,
-						dname_len + 1);
-					/* rc = 0 indicates found */
-					rc = NOERR;
-				} else {
-					/*
-					 * The fc_host is too large
-					 * for the buffer.
-					 */
-					rc = EINTERR;
-				}
-			}
-		}
-		free(namelist[n]);
-
-	}
-	free(namelist);
-
-	return rc;
-}
-
-enum fcoe_err fcoe_validate_interface(char *ifname)
-{
-	enum fcoe_err rc = NOERR;
-	char path[MAX_PATH_LEN];
-
-
-	if (!strlen(ifname))
-		rc = ENOETHDEV;
-
-	/*
-	 * TODO: Is there a better way to check if the
-	 * interface name is correct?
-	 */
-	sprintf(path, "%s/%s", SYSFS_NET, ifname);
-	if (!rc && fcoe_checkdir(path))
-		rc = ENOETHDEV;
-
-	return rc;
-}
-
-/*
- * Validate an existing instance for an FC interface
- */
-enum fcoe_err fcoe_validate_fcoe_conn(char *ifname)
-{
-	char fchost[FCHOSTBUFLEN];
-	enum fcoe_err rc = NOERR;
-
-	rc = fcoe_validate_interface(ifname);
-
-	if (!rc)
-		rc = fcoe_find_fchost(ifname, fchost, FCHOSTBUFLEN);
-
-	return rc;
-}
-
-/*
- * Open and close to check if directory exists
- */
-int fcoe_checkdir(char *dir)
-{
-	DIR *d = NULL;
-
-	if (!dir)
-		return -EINVAL;
-	/* check if we have sysfs */
-	d = opendir(dir);
-	if (!d)
-		return -EINVAL;
-	closedir(d);
-	return 0;
-}
-
-char *get_ifname_from_symbolic_name(const char *symbolic_name)
-{
-	int symbolic_name_len = strlen(symbolic_name);
-	int lead_len = strlen(SYMB_NAME_LEAD);
-
-	if (lead_len < symbolic_name_len)
-		return (char *)(symbolic_name + lead_len);
-
-       return NULL;
-}
-
-int check_symbolic_name_for_interface(const char *symbolic_name,
-				      const char *ifname)
-{
-	int rc = -EINVAL;
-	char *symb;
-
-	if (!ifname)
-		return rc;
-
-	symb = get_ifname_from_symbolic_name(symbolic_name);
-
-	/*
-	 * It's important to use the length of the ifname
-	 * from the symbolic_name here. If the ifname length
-	 * were used then if the user passed in a substring
-	 * of the the interface name it would match because
-	 * we'd only be looking for the first few characters,
-	 * not the whole string.
-	 */
-	if (symb && !strncmp(ifname, symb, strlen(symb)))
-		rc = 0;
-
-	return rc;
-}
diff --git a/fcoe_utils.h b/fcoe_utils.h
deleted file mode 100644
index 3c43304..0000000
--- a/fcoe_utils.h
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * Copyright(c) 2010 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 _FCOE_UTILS_H_
-#define _FCOE_UTILS_H_
-
-#ifndef _GNU_SOURCE
-#define _GNU_SOURCE
-#endif
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <strings.h>
-#include <libgen.h>
-#include <dirent.h>
-#include <errno.h>
-
-/*
- * Used when trying to get the interface name from the symbolic_name.
- * Not very elegant as this code will need to change if fcoe.ko changes
- * its version.
- */
-#define FCOE_MODULE_VERSION "v0.1"
-#define SYMB_NAME_LEAD "fcoe " FCOE_MODULE_VERSION " over "
-
-#define MAX_STR_LEN 512
-#define MAX_PATH_LEN MAX_STR_LEN
-
-#define SYSFS_MOUNT	"/sys"
-#define SYSFS_NET	SYSFS_MOUNT "/class/net"
-#define SYSFS_FCHOST	SYSFS_MOUNT "/class/fc_host"
-#define SYSFS_FCOE	SYSFS_MOUNT "/module/fcoe/parameters"
-
-#define FCHOSTBUFLEN 64
-
-/*
- * This macro assumes that progname has been set
- */
-#define FCOE_LOG_ERR(fmt, args...)					\
-	do {								\
-		fprintf(stderr, "%s: " fmt, progname, ##args);		\
-	} while (0)
-
-
-enum fcoe_err {
-	NOERR = 0,    /* No error */
-	EFCOECONN,    /* FCoE connection already exists */
-	ENOFCOECONN,  /* No FCoE connection on interface */
-	EINTERR,      /* Internal error */
-	EINVALARG,    /* Invalid argument */
-	EBADNUMARGS,  /* Invalid number of arguments */
-	EIGNORE,      /* Ignore this error value */
-	ENOSYSFS,     /* sysfs is not present */
-	ENOETHDEV,    /* Not a valid Ethernet interface */
-	ENOMONCONN,   /* Not connected to fcoemon */
-	ECONNTMOUT,   /* Connection to fcoemon timed out */
-	EHBAAPIERR,   /* Error using HBAAPI/libhbalinux */
-};
-
-enum fcoe_err fcoe_validate_interface(char *ifname);
-enum fcoe_err fcoe_validate_fcoe_conn(char *ifname);
-enum fcoe_err fcoe_find_fchost(char *ifname, char *fchost, int len);
-int fcoe_checkdir(char *dir);
-int check_symbolic_name_for_interface(const char *symbolic_name,
-				      const char *ifname);
-char *get_ifname_from_symbolic_name(const char *symbolic_name);
-
-#endif /* _FCOE_UTILS_H_ */
diff --git a/fcoe_utils_version.h.in b/fcoe_utils_version.h.in
deleted file mode 100644
index 642f37d..0000000
--- a/fcoe_utils_version.h.in
+++ /dev/null
@@ -1,6 +0,0 @@
-#ifndef _FCOE_UTILS_VERSION_H_
-#define _FCOE_UTILS_VERSION_H_
-
-#define FCOE_UTILS_VERSION "@VERSION@"
-
-#endif /* _FCOE_UTILS_VERSION_H_ */
diff --git a/fcoemon.c b/fcoemon.c
index 8cb31ea..5af8507 100644
--- a/fcoemon.c
+++ b/fcoemon.c
@@ -55,6 +55,9 @@
 #include "fcoemon.h"
 #include "fcoe_clif.h"
 
+#include "fip.h"
+#include "rtnetlink.h"
+
 #ifndef SYSCONFDIR
 #define SYSCONFDIR                  "/etc"
 #endif
@@ -103,12 +106,16 @@ struct fcoe_port {
 					is a VLAN */
 	int fcoe_enable;
 	int dcb_required;
+	int auto_vlan;
 
 	/* following track data required to manage FCoE interface state */
 	enum fcp_action action;      /* current state */
 	enum fcp_action last_action; /* last action */
 	int last_msg_type;     /* last rtnetlink msg type received on if name */
 	struct sock_info *sock_reply;
+
+	int ifindex;
+	unsigned char mac[ETHER_ADDR_LEN];
 };
 
 enum fcoeport_ifname {
@@ -153,6 +160,8 @@ static void fcm_link_getlink(void);
 static int fcm_link_buf_check(size_t);
 static void clear_dcbd_info(struct fcm_netif *ff);
 
+static int fcm_fip_socket;
+
 /*
  * Table for getopt_long(3).
  */
@@ -350,9 +359,21 @@ static int fcm_read_config_files(void)
 			continue;
 		}
 		/* if not found, default to "no" */
-		if (!strncasecmp(val, "yes", 3) && rc == 1) {
+		if (!strncasecmp(val, "yes", 3) && rc == 1)
 			next->dcb_required = 1;
+
+		/* AUTO_VLAN */
+		rc = fcm_read_config_variable(file, val, sizeof(val),
+					      fp, "AUTO_VLAN");
+		if (rc < 0) {
+			FCM_LOG("%s invalid format for AUTO_VLAN setting");
+			fclose(fp);
+			free(next);
+			continue;
 		}
+		/* if not found, default to "no" */
+		if (!strncasecmp(val, "yes", 3) && rc == 1)
+			next->auto_vlan = 1;
 
 		fclose(fp);
 
@@ -460,6 +481,92 @@ static int fcm_link_init(void)
 	return 0;
 }
 
+static struct fcoe_port *fcm_port_create(char *ifname, int cmd);
+
+void fcm_new_vlan(int ifindex, int vid)
+{
+	char real_name[IFNAMSIZ];
+	char vlan_name[IFNAMSIZ];
+
+	FCM_LOG_DBG("Auto VLAN Found FCF on VID %d\n", vid);
+
+	if (rtnl_find_vlan(ifindex, vid, vlan_name)) {
+		rtnl_get_linkname(ifindex, real_name);
+		snprintf(vlan_name, IFNAMSIZ, "%s.%d-fcoe", real_name, vid);
+		vlan_create(ifindex, vid, vlan_name);
+	}
+	rtnl_set_iff_up(0, vlan_name);
+	fcm_port_create(vlan_name, FCP_CREATE_IF);
+}
+
+
+int fcm_vlan_disc_handler(struct fiphdr *fh, struct sockaddr_ll *sa, void *arg)
+{
+	int vid;
+	unsigned char mac[ETHER_ADDR_LEN];
+	int len = ntohs(fh->fip_desc_len);
+	struct fip_tlv_hdr *tlv = (struct fip_tlv_hdr *)(fh + 1);
+
+	FCM_LOG_DBG("%s", __func__);
+
+	if (ntohs(fh->fip_proto) != FIP_PROTO_VLAN) {
+		FCM_LOG_DBG("ignoring FIP frame that is not of type VLAN");
+		return -1;
+	}
+
+	if (fh->fip_subcode != FIP_VLAN_NOTE) {
+		FCM_LOG_DBG("ignoring FIP VLAN Discovery Request");
+		return -1;
+	}
+
+	while (len > 0) {
+		switch (tlv->tlv_type) {
+		case FIP_TLV_MAC_ADDR:
+			memcpy(mac, ((struct fip_tlv_mac_addr *)tlv)->mac_addr,
+			       ETHER_ADDR_LEN);
+			break;
+		/*
+		 * this expects to see the MAC_ADDR TLV first,
+		 * and is broken if not
+		 */
+		case FIP_TLV_VLAN:
+			if (tlv->tlv_len != 1) {
+				FCM_LOG_ERR(EINVAL, "bad length on VLAN TLV");
+				break;
+			}
+			vid = ntohs(((struct fip_tlv_vlan *)tlv)->vlan);
+			fcm_new_vlan(sa->sll_ifindex, vid);
+			break;
+		default:
+			/* unexpected or unrecognized descriptor */
+			FCM_LOG_DBG("ignoring TLV type %d", tlv->tlv_type);
+			break;
+		}
+		len -= tlv->tlv_len;
+		tlv = ((void *) tlv) + (tlv->tlv_len << 2);
+	};
+	return 0;
+}
+
+static void fcm_fip_recv(void *arg)
+{
+	fip_recv(fcm_fip_socket, fcm_vlan_disc_handler, NULL);
+}
+
+static int fcm_vlan_disc_init(void)
+{
+	int fd;
+
+	fd = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_FIP));
+	if (fd < 0) {
+		FCM_LOG_ERR(errno, "socket error");
+		return fd;
+	}
+	fcm_fip_socket = fd;
+	sa_select_add_fd(fd, fcm_fip_recv, NULL, NULL, NULL);
+	return 0;
+}
+
 
 /* fcm_vlan_dev_real_dev - query vlan real_dev
  * @vlan_ifname - vlan device ifname to find real interface name for
@@ -542,7 +649,10 @@ static void fcp_set_next_action(struct fcoe_port *p, enum fcp_action action)
 			p->action = action;
 			break;
 		case FCP_ACTIVATE_IF:
-			p->action = FCP_ENABLE_IF;
+			if (p->auto_vlan)
+				p->action = FCP_VLAN_DISC;
+			else
+				p->action = FCP_ENABLE_IF;
 			break;
 		default:
 			p->action = FCP_WAIT;
@@ -552,10 +662,11 @@ static void fcp_set_next_action(struct fcoe_port *p, enum fcp_action action)
 	case FCP_DESTROY_IF:
 		switch (action) {
 		case FCP_CREATE_IF:
-			p->action = action;
-			break;
 		case FCP_ACTIVATE_IF:
-			p->action = FCP_CREATE_IF;
+			if (p->auto_vlan)
+				p->action = FCP_VLAN_DISC;
+			else
+				p->action = FCP_CREATE_IF;
 			break;
 		default:
 			p->action = FCP_WAIT;
@@ -578,12 +689,15 @@ static void fcp_set_next_action(struct fcoe_port *p, enum fcp_action action)
 	case FCP_DISABLE_IF:
 		switch (action) {
 		case FCP_DESTROY_IF:
-		case FCP_ENABLE_IF:
 		case FCP_RESET_IF:
 			p->action = action;
 			break;
+		case FCP_ENABLE_IF:
 		case FCP_ACTIVATE_IF:
-			p->action = FCP_ENABLE_IF;
+			if (p->auto_vlan)
+				p->action = FCP_VLAN_DISC;
+			else
+				p->action = FCP_ENABLE_IF;
 			break;
 		default:
 			p->action = FCP_WAIT;
@@ -594,14 +708,30 @@ static void fcp_set_next_action(struct fcoe_port *p, enum fcp_action action)
 	case FCP_SCAN_IF:
 		switch (action) {
 		case FCP_DESTROY_IF:
-		case FCP_ENABLE_IF:
 		case FCP_DISABLE_IF:
 		case FCP_RESET_IF:
 		case FCP_SCAN_IF:
 			p->action = action;
 			break;
+		case FCP_ENABLE_IF:
 		case FCP_ACTIVATE_IF:
-			p->action = FCP_ENABLE_IF;
+			if (p->auto_vlan)
+				p->action = FCP_VLAN_DISC;
+			else
+				p->action = FCP_ENABLE_IF;
+			break;
+		default:
+			p->action = FCP_WAIT;
+			break;
+		}
+		break;
+	case FCP_VLAN_DISC:
+		switch (action) {
+		case FCP_DESTROY_IF:
+		case FCP_DISABLE_IF:
+		case FCP_RESET_IF:
+		case FCP_SCAN_IF:
+			p->action = action;
 			break;
 		default:
 			p->action = FCP_WAIT;
@@ -727,12 +857,15 @@ void fcm_process_link_msg(struct ifinfomsg *ip, int len, unsigned type)
 	char ifname[IFNAMSIZ];
 	char real_dev[IFNAMSIZ];
 	u_int8_t operstate;
-	u_int64_t mac;
+	unsigned char mac[ETHER_ADDR_LEN];
 	int is_vlan;
+	int ifindex;
 
-	mac = is_vlan = 0;
+	is_vlan = 0;
 	operstate = IF_OPER_UNKNOWN;
 
+	ifindex = ip->ifi_index;
+
 	if (ip->ifi_type != ARPHRD_ETHER)
 		return;
 
@@ -742,7 +875,7 @@ void fcm_process_link_msg(struct ifinfomsg *ip, int len, unsigned type)
 		switch (ap->rta_type) {
 		case IFLA_ADDRESS:
 			if (RTA_PAYLOAD(ap) == 6)
-				mac = net48_get(RTA_DATA(ap));
+				memcpy(mac, RTA_DATA(ap), ETHER_ADDR_LEN);
 			break;
 
 		case IFLA_IFNAME:
@@ -772,6 +905,12 @@ void fcm_process_link_msg(struct ifinfomsg *ip, int len, unsigned type)
 		if (!p)
 			return;
 
+		p->ifindex = ifindex;
+		memcpy(p->mac, mac, ETHER_ADDR_LEN);
+
+		/* don't do VLAN discovery on a VLAN */
+		p->auto_vlan = 0;
+
 		/* try to find the real device name */
 		real_dev[0] = '\0';
 		fcm_vlan_dev_real_dev(ifname, real_dev);
@@ -784,6 +923,8 @@ void fcm_process_link_msg(struct ifinfomsg *ip, int len, unsigned type)
 		 * an FCoE interface configured on it.
 		 */
 		if (p) {
+			p->ifindex = ifindex;
+			memcpy(p->mac, mac, ETHER_ADDR_LEN);
 			strncpy(p->real_ifname, ifname, strlen(ifname)+1);
 			update_fcoe_port_state(p, type, operstate,
 					       FCP_REAL_IFNAME);
@@ -1795,6 +1936,13 @@ err_out:
 	return ret;
 }
 
+int fcm_start_vlan_disc(struct fcoe_port *p)
+{
+	FCM_LOG_DBG("%s", __func__);
+	fip_send_vlan_request(fcm_fip_socket, p->ifindex, p->mac);
+	return 0;
+}
+
 /*
  *
  * Input:  action = 1      Destroy the FCoE interface
@@ -1857,6 +2005,10 @@ static void fcm_fcoe_action(struct fcm_netif *ff, struct fcoe_port *p)
 			SYSFS_FCHOST, fchost, fchost);
 		rc = fcm_fcoe_if_action(path, "- - -");
 		break;
+	case FCP_VLAN_DISC:
+		FCM_LOG_DBG("OP: VLAN DISC %s\n", p->ifname);
+		rc = fcm_start_vlan_disc(p);
+		break;
 	default:
 		return;
 		break;
@@ -2070,7 +2222,7 @@ static void fcm_pidfile_create(void)
 	}
 }
 
-static int fcm_cli_create(char *ifname, int cmd, struct sock_info **r)
+static struct fcoe_port *fcm_port_create(char *ifname, int cmd)
 {
 	struct fcoe_port *p;
 	struct fcoe_port *curr;
@@ -2081,11 +2233,10 @@ static int fcm_cli_create(char *ifname, int cmd, struct sock_info **r)
 		if (!p->fcoe_enable) {
 			p->fcoe_enable = 1;
 			fcp_set_next_action(p, cmd);
-			p->sock_reply = *r;
 			if (p->dcb_required) {
 				ff = fcm_netif_lookup(p->real_ifname);
 				if (!ff)
-					return fcm_success;
+					return p;
 				fcm_dcbd_state_set(ff, FCD_GET_DCB_STATE);
 				if (ff->ff_dcbd_state == FCD_GET_DCB_STATE)
 					fcp_set_next_action(p, FCP_WAIT);
@@ -2094,13 +2245,13 @@ static int fcm_cli_create(char *ifname, int cmd, struct sock_info **r)
 			p->fcoe_enable = 1;
 			fcp_set_next_action(p, cmd);
 		}
-		return fcm_success;
+		return p;
 	}
 
 	p = alloc_fcoe_port(ifname);
 	if (!p) {
 		FCM_LOG_ERR(errno, "fail to allocate fcoe_port %s", ifname);
-		return fcm_fail;
+		return NULL;
 	}
 
 	fcm_vlan_dev_real_dev(ifname, p->real_ifname);
@@ -2109,7 +2260,6 @@ static int fcm_cli_create(char *ifname, int cmd, struct sock_info **r)
 	p->fcoe_enable = 1;
 	p->dcb_required = 0;
 	fcp_set_next_action(p, cmd);
-	p->sock_reply = *r;
 	p->next = NULL;
 
 	if (!fcoe_config.port)
@@ -2125,9 +2275,19 @@ static int fcm_cli_create(char *ifname, int cmd, struct sock_info **r)
 	ff = fcm_netif_lookup_create(p->real_ifname);
 	if (!ff) {
 		FCM_LOG_ERR(errno, "fail to allocate fcm_netif %s", ifname);
-		return fcm_fail;
+		return NULL;
 	}
+	return p;
+}
+
+static int fcm_cli_create(char *ifname, int cmd, struct sock_info **r)
+{
+	struct fcoe_port *p;
 
+	p = fcm_port_create(ifname, cmd);
+	if (!p)
+		return fcm_fail;
+	p->sock_reply = *r;
 	return fcm_success;
 }
 
@@ -2350,6 +2510,7 @@ int main(int argc, char **argv)
 			break;
 		case 'd':
 			fcoe_config.debug = 1;
+			enable_debug_log(1);
 			break;
 		case 's':
 			fcoe_config.use_syslog = 1;
@@ -2421,6 +2582,7 @@ int main(int argc, char **argv)
 	fcm_fcoe_init();
 	fcm_link_init();	/* NETLINK_ROUTE protocol */
 	fcm_dcbd_init();
+	fcm_vlan_disc_init();
 	fcm_srv_create(&srv_info);
 	sa_select_set_callback(fcm_handle_changes);
 
diff --git a/fcoemon.h b/fcoemon.h
index a36128d..9917d76 100644
--- a/fcoemon.h
+++ b/fcoemon.h
@@ -43,14 +43,12 @@ struct fcoe_config {
 
 #define FCM_LOG_DBG(fmt, args...)					\
 	do {								\
-		if (fcoe_config.debug)					\
-			sa_log(fmt, ##args);				\
+		sa_log_debug(fmt, ##args);				\
 	} while (0)
 
 #define FCM_LOG_DEV_DBG(fcm, fmt, args...)				\
 	do {								\
-		if (fcoe_config.debug)					\
-			sa_log("%s, " fmt, fcm->ifname, ##args);	\
+		sa_log_debug("%s, " fmt, fcm->ifname, ##args);	\
 	} while (0)
 
 #define FCM_LOG_DEV(fcm, fmt, args...)				\
@@ -95,6 +93,7 @@ enum fcp_action {
    FCP_DISABLE_IF,      /* disable FCoE interface */
    FCP_ACTIVATE_IF,     /* create or enable FCoE interface */
    FCP_ERROR,           /* error condition */
+   FCP_VLAN_DISC,       /* start VLAN discovery */
 };
 
 #define FCM_DCBD_STATES {                         \
diff --git a/fcoemon_utils.c b/fcoemon_utils.c
deleted file mode 100644
index 5f1cd10..0000000
--- a/fcoemon_utils.c
+++ /dev/null
@@ -1,688 +0,0 @@
-/*
- * 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 "fcoemon_utils.h"
-#include "net_types.h"
-#include "fc_types.h"
-
-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.
- */
-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
- */
-void
-sa_log(const char *format, ...)
-{
-	va_list arg;
-
-	va_start(arg, format);
-	sa_log_va(NULL, format, arg);
-	va_end(arg);
-}
-
-/*
- * log with error number.
- */
-void
-sa_log_err(int error, const char *func, const char *format, ...)
-{
-	va_list arg;
-	char buf[SA_LOG_BUF_LEN];
-
-	if (func)
-		sa_log("%s: error %d %s", func, error,
-		       strerror_r(error, buf, sizeof(buf)));
-	else
-		sa_log("error %d %s", error,
-		       strerror_r(error, buf, sizeof(buf)));
-	va_start(arg, format);
-	sa_log_va(func, format, arg);
-	va_end(arg);
-}
-
-/*
- * 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 */
-
-/*
- * Assert failures.
- */
-void
-assert_failed(const char *format, ...)
-{
-	va_list arg;
-	char buf[SA_LOG_BUF_LEN];
-
-	va_start(arg, format);
-	vsnprintf(buf, sizeof(buf), format, arg);
-	va_end(arg);
-	sa_log_abort(buf);
-}
-
-/*
- * Log options.
- * These may be set directly by callers.
- */
-u_int sa_log_flags;                     /* timestamp and other option flags */
-int sa_log_time_delta_min = 1;          /* minimum diff to print in millisec */
-char *sa_log_prefix;                    /* string to print before any message */
-
-void
-sa_log_set_option(u_int flags)
-{
-	sa_log_flags = flags;
-}
-
-/*
- * Put timestamp on front of each log line, as controlled by tunables above.
- */
-static void
-sa_log_timestamp(void)
-{
-	static struct timeval tlast;
-	char ctime_buf[30];
-	struct timeval t;
-	struct timeval diff;
-
-	gettimeofday(&t, NULL);
-	if (sa_log_flags & SA_LOGF_TIME) {
-		ctime_r(&t.tv_sec, ctime_buf);
-		ctime_buf[11 + 8] = '\0';   /* trim ctime after seconds */
-		fprintf(stderr, "%s.%3.3ld ",
-			ctime_buf + 11, t.tv_usec / 1000);
-	}
-	if (sa_log_flags & SA_LOGF_DELTA) {
-		if (tlast.tv_sec == 0)
-			tlast = t;
-		timersub(&t, &tlast, &diff);
-		tlast = t;
-		if (diff.tv_sec != 0 ||
-		    diff.tv_usec >= sa_log_time_delta_min * 1000)
-			fprintf(stderr, "%4ld.%3.3ld ",
-				diff.tv_sec, diff.tv_usec / 1000);
-		else
-			fprintf(stderr, "%8s ", "");
-	}
-	if (sa_log_prefix)
-		fprintf(stderr, "%s: ", sa_log_prefix);
-}
-
-void
-sa_log_output(const char *buf)
-{
-	if (use_syslog) {
-		syslog(LOG_INFO, "%s", buf);
-		return;
-	}
-	sa_log_timestamp();
-	fprintf(stderr, "%s", buf);
-	fflush(stderr);
-}
-
-void
-sa_log_abort(const char *buf)
-{
-	sa_log_output(buf);
-	abort();
-}
-
-/*
- * Make a printable NUL-terminated copy of the string.
- * The source buffer might not be NUL-terminated.
- */
-char *
-sa_strncpy_safe(char *dest, size_t len, const char *src, size_t src_len)
-{
-	char *dp = dest;
-	const char *sp = src;
-
-	while (len-- > 1 && src_len-- > 0 && *sp != '\0') {
-		*dp++ = isprint(*sp) ? *sp : (isspace(*sp) ? ' ' : '.');
-		sp++;
-	}
-	*dp = '\0';
-
-	/*
-	 * Take off trailing blanks.
-	 */
-	while (--dp >= dest && isspace(*dp))
-		*dp = '\0';
-	return dest;
-}
-
-/** sa_enum_decode(buf, len, tp, val)
- *
- * @param buf buffer for result (may be used or not).
- * @param len size of buffer (at least 32 bytes recommended).
- * @param tp pointer to table of names and values, struct sa_nameval.
- * @param val value to be decoded into a name.
- * @returns pointer to name string.  Unknown values are put into buffer in hex.
- */
-const char *
-sa_enum_decode(char *buf, size_t len, const struct sa_nameval *tp, u_int val)
-{
-	for (; tp->nv_name != NULL; tp++) {
-		if (tp->nv_val == val)
-			return tp->nv_name;
-	}
-	snprintf(buf, len, "Unknown (code 0x%X)", val);
-	return buf;
-}
-
-#define SA_TIMER_HZ     (1000 * 1000 * 1000ULL)	/* nanoseconds per second */
-#define SA_TIMER_FUZZ   (500 * 1000ULL)	/* 500 microseconds is close enough */
-
-static struct sa_timer *sa_timer_head;	/* queue of scheduled events */
-static u_int64_t sa_timer_nsec;		/* nanoseconds since start */
-
-/*
- * Initialize a timer structure.  Set handler.
- */
-void
-sa_timer_init(struct sa_timer *tm, void (*handler)(void *), void *arg)
-{
-	ASSERT(handler != NULL);
-	memset(tm, 0, sizeof(*tm));
-	tm->tm_handler = handler;
-	tm->tm_handler_arg = arg;
-}
-
-/*
- * Allocate a timer structure.  Set handler.
- */
-struct sa_timer *
-sa_timer_alloc(void (*handler)(void *arg), void *arg)
-{
-	struct sa_timer *tm;
-
-	tm = malloc(sizeof(*tm));
-	if (tm)
-		sa_timer_init(tm, handler, arg);
-	return tm;
-}
-
-u_int64_t
-sa_timer_get(void)
-{
-	u_int64_t nsec;
-#ifndef _POSIX_TIMERS
-	struct timeval tv;
-
-	gettimeofday(&tv, NULL);	/* XXX want monotonic time, not TOD */
-	nsec = tv.tv_sec * SA_TIMER_HZ + tv.tv_usec * 1000;
-#else /* _POSIX_TIMERS */
-	struct timespec ts;
-	int rc;
-
-	rc = clock_gettime(CLOCK_MONOTONIC, &ts);
-	ASSERT_NOTIMPL(rc == 0);
-	nsec = ts.tv_sec * SA_TIMER_HZ + ts.tv_nsec;
-#endif /* _POSIX_TIMERS */
-
-#if 0 /* XXX */
-	ASSERT(nsec >= sa_timer_nsec);	/* really must be monotonic */
-#else
-	if (nsec < sa_timer_nsec)
-		sa_log("sa_timer_get: negative time lapse "
-			"old %qud new %qud diff %qd nsec\n",
-			(long long unsigned int) sa_timer_nsec,
-			(long long unsigned int) nsec,
-			(long long int) (nsec - sa_timer_nsec));
-#endif
-	sa_timer_nsec = nsec;
-	return nsec;
-}
-
-/*
- * Get monotonic time since some arbitrary time in the past.
- * If _POSIX_MONOTONIC_CLOCK isn't available, we'll use time of day.
- */
-u_int
-sa_timer_get_secs(void)
-{
-	u_int sec;
-
-#ifndef _POSIX_TIMERS
-	struct timeval tv;
-
-	gettimeofday(&tv, NULL); /* XXX want monotonic time, not TOD */
-	sec = tv.tv_sec;
-#else /* _POSIX_TIMERS */
-	struct timespec ts;
-	int rc;
-
-	rc = clock_gettime(CLOCK_MONOTONIC, &ts);
-	ASSERT_NOTIMPL(rc == 0);
-	sec = ts.tv_sec;
-#endif /* _POSIX_TIMERS */
-	return sec;
-}
-
-/*
- * Set timer to fire.   Delta is in microseconds from now.
- */
-void
-sa_timer_set(struct sa_timer *tm, u_long delta_usec)
-{
-	struct sa_timer *cur;
-	struct sa_timer **prev;
-
-	ASSERT(delta_usec != 0);
-	ASSERT(tm->tm_handler != NULL);
-	sa_timer_cancel(tm);
-	ASSERT(sa_timer_active(tm) == 0);
-	tm->tm_nsec =
-	    sa_timer_get() + delta_usec * SA_TIMER_HZ / SA_TIMER_UNITS;
-	ASSERT(tm->tm_nsec != 0);
-
-	/*
-	 * Insert timer into sorted linked list.
-	 * Find insertion point, before cur.
-	 */
-	for (prev = &sa_timer_head;
-	     (cur = *prev) != NULL && cur->tm_nsec <= tm->tm_nsec;
-	     prev = &cur->tm_next)
-		;
-	*prev = tm;
-	tm->tm_next = cur;
-}
-
-/*
- * Cancel timer if it is active.
- */
-void
-sa_timer_cancel(struct sa_timer *tm)
-{
-	struct sa_timer *cur;
-	struct sa_timer **prev;
-
-	if (sa_timer_active(tm)) {
-		for (prev = &sa_timer_head; (cur = *prev) != NULL;
-		     prev = &cur->tm_next)
-			if (cur == tm) {
-				tm->tm_nsec = 0;
-				*prev = tm->tm_next;
-				break;
-			}
-		ASSERT(cur == tm);
-	}
-}
-
-/*
- * Free (and cancel) timer.
- */
-void
-sa_timer_free(struct sa_timer *tm)
-{
-	if (sa_timer_active(tm))
-		sa_timer_cancel(tm);
-	free(tm);
-}
-
-/*
- * Handle timer checks.  Called from select loop or other periodic function.
- *
- * The struct timeval is set before returning to the maximum amount of time
- * that should elapse before the next call.
- *
- * Returns 1 if any timer functions were called, 0 otherwise.
- */
-int
-sa_timer_check(struct timeval *tv)
-{
-	u_int64_t now = 0;
-	u_int64_t next_due = 0;
-	struct sa_timer *tm;
-	int ret = 0;
-
-	/*
-	 * Remember, the list may change during the handler.
-	 */
-	for (;;) {
-		now = sa_timer_get();
-		tm = sa_timer_head;
-		if (tm == NULL) {
-			next_due = now;
-			break;
-		}
-
-		next_due = tm->tm_nsec;
-		if (next_due > now + SA_TIMER_FUZZ)
-			break;
-
-		/*
-		 * Remove this element from the list.
-		 */
-		sa_timer_head = tm->tm_next;
-		tm->tm_next = NULL;
-
-		/*
-		 * Mark cancelled and call handler.
-		 */
-		tm->tm_nsec = 0;
-		ASSERT(tm->tm_handler != NULL);
-		(*tm->tm_handler)(tm->tm_handler_arg);
-		ret = 1;
-	}
-
-	ASSERT(next_due >= now);
-	next_due -= now;
-	tv->tv_sec = (time_t) (next_due / SA_TIMER_HZ);
-	tv->tv_usec = (long) (next_due % SA_TIMER_HZ) / 1000;
-
-	return ret;
-}
-
-#define NFC_NFDS        64
-
-/*
- * Deferred procedure call.
- */
-struct sa_defer_ent {
-	TAILQ_ENTRY(sa_defer_ent) de_next;
-	void        (*de_func)(void *arg);
-	void        *de_arg;
-};
-
-/*
-	* Static module state.
-	*/
-static struct sa_sel_state {
-	fd_set      ts_rx_fds;
-	fd_set      ts_tx_fds;
-	fd_set      ts_ex_fds;
-	int         ts_max_fd;
-	u_char      ts_exit;
-	struct sa_sel_fd {
-		void    (*ts_rx_handler)(void *);
-		void    (*ts_tx_handler)(void *);
-		void    (*ts_ex_handler)(void *);
-		void    *ts_handler_arg;
-	} ts_fd[NFC_NFDS];
-	void        (*ts_callback)(void);
-	TAILQ_HEAD(, sa_defer_ent) ts_defer_list;
-} sa_sel_state;
-
-int sa_select_loop(void)
-{
-	struct sa_sel_state *ss = &sa_sel_state;
-	struct sa_sel_fd *fp;
-	fd_set rx_fds;
-	fd_set tx_fds;
-	fd_set ex_fds;
-	struct timeval tval;
-	struct timeval *tvp;
-	int rv, i;
-
-	ss->ts_exit = 0;
-	while (ss->ts_exit == 0) {
-		sa_timer_check(&tval);
-		if (ss->ts_exit)
-			break;
-		if (tval.tv_sec == 0 && tval.tv_usec == 0)
-			tvp = NULL;
-		else
-			tvp = &tval;
-		rx_fds = ss->ts_rx_fds;
-		tx_fds = ss->ts_tx_fds;
-		ex_fds = ss->ts_ex_fds;
-		rv = select(ss->ts_max_fd + 1, &rx_fds, &tx_fds, &ex_fds, tvp);
-		if (rv == -1) {
-			if (errno == EINTR)
-				continue;
-			return errno;
-		}
-
-		fp = ss->ts_fd;
-		for (i = 0; rv > 0 && i <= sa_sel_state.ts_max_fd; i++, fp++) {
-			if (FD_ISSET(i, &rx_fds)) {
-				if (fp->ts_rx_handler != NULL)
-					(*fp->ts_rx_handler)
-					(fp->ts_handler_arg);
-				else
-					ASSERT(!FD_ISSET(i, &ss->ts_rx_fds));
-				--rv;
-			}
-			if (FD_ISSET(i, &tx_fds)) {
-				if (fp->ts_tx_handler != NULL)
-					(*fp->ts_tx_handler)
-					(fp->ts_handler_arg);
-				else
-					ASSERT(!FD_ISSET(i, &ss->ts_tx_fds));
-				--rv;
-			}
-			if (FD_ISSET(i, &ex_fds)) {
-				if (fp->ts_ex_handler != NULL)
-					(*fp->ts_ex_handler)
-					(fp->ts_handler_arg);
-				else
-					ASSERT(!FD_ISSET(i, &ss->ts_ex_fds));
-				--rv;
-			}
-		}
-		if (ss->ts_callback != NULL)
-			(*ss->ts_callback)();
-	}
-	return 0;
-}
-
-void
-sa_select_add_fd(int fd,
-		 void (*rx_handler)(void *),
-		 void (*tx_handler)(void *),
-		 void (*ex_handler)(void *),
-		 void *arg)
-{
-	struct sa_sel_state *ss = &sa_sel_state;
-	struct sa_sel_fd *fp;
-
-	ASSERT_NOTIMPL(fd < NFC_NFDS);
-	ASSERT(rx_handler != NULL || tx_handler != NULL || ex_handler != NULL);
-	if (ss->ts_max_fd < fd)
-		ss->ts_max_fd = fd;
-	fp = &ss->ts_fd[fd];
-	fp->ts_handler_arg = arg;
-	if (rx_handler != NULL) {
-		fp->ts_rx_handler = rx_handler;
-		FD_SET(fd, &ss->ts_rx_fds);
-	}
-	if (tx_handler != NULL) {
-		fp->ts_tx_handler = tx_handler;
-		FD_SET(fd, &ss->ts_tx_fds);
-	}
-	if (ex_handler != NULL) {
-		fp->ts_ex_handler = ex_handler;
-		FD_SET(fd, &ss->ts_ex_fds);
-	}
-}
-
-void
-sa_select_set_rx(int fd, void (*handler)(void *))
-{
-	struct sa_sel_state *ss = &sa_sel_state;
-
-	ASSERT(fd <= ss->ts_max_fd);
-	ss->ts_fd[fd].ts_rx_handler = handler;
-	if (handler != NULL)
-		FD_SET(fd, &ss->ts_rx_fds);
-	else
-		FD_CLR(fd, &ss->ts_rx_fds);
-}
-
-void
-sa_select_set_tx(int fd, void (*handler)(void *))
-{
-	struct sa_sel_state *ss = &sa_sel_state;
-
-	ASSERT(fd <= ss->ts_max_fd);
-	ss->ts_fd[fd].ts_tx_handler = handler;
-	if (handler != NULL)
-		FD_SET(fd, &ss->ts_tx_fds);
-	else
-		FD_CLR(fd, &ss->ts_tx_fds);
-}
-
-void
-sa_select_set_ex(int fd, void (*handler)(void *))
-{
-	struct sa_sel_state *ss = &sa_sel_state;
-
-	ASSERT(fd <= ss->ts_max_fd);
-	ss->ts_fd[fd].ts_ex_handler = handler;
-	if (handler != NULL)
-		FD_SET(fd, &ss->ts_ex_fds);
-	else
-		FD_CLR(fd, &ss->ts_ex_fds);
-}
-
-void
-sa_select_rem_fd(int fd)
-{
-	struct sa_sel_state *ss = &sa_sel_state;
-	struct sa_sel_fd *fp;
-
-	ASSERT_NOTIMPL(fd < NFC_NFDS);
-	FD_CLR(fd, &ss->ts_rx_fds);
-	FD_CLR(fd, &ss->ts_tx_fds);
-	FD_CLR(fd, &ss->ts_ex_fds);
-	fp = &ss->ts_fd[fd];
-	fp->ts_rx_handler = NULL;
-	fp->ts_tx_handler = NULL;
-	fp->ts_ex_handler = NULL;
-	fp->ts_handler_arg = NULL;
-}
-
-/*
- * Set callback for every time through the select loop.
- */
-void
-sa_select_set_callback(void (*cb)(void))
-{
-	sa_sel_state.ts_callback = cb;
-}
-
-/*
- * Cause select loop to exit.
- * This is invoked from a handler which wants the select loop to return
- * after the handler is finished.  For example, during receipt of a network
- * packet, the program may decide to clean up and exit, but in order to do
- * this cleanly, all lower-level protocol handlers should return first.
- */
-void
-sa_select_exit(void)
-{
-	sa_sel_state.ts_exit = 1;
-}
-
-/*
- * Convert 48-bit IEEE MAC address to 64-bit FC WWN.
- */
-fc_wwn_t
-fc_wwn_from_mac(u_int64_t mac, u_int scheme, u_int 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_NOTREACHED;
-		break;
-	}
-	return wwn;
-}
-
-/* assumes input is pointer to two hex digits */
-/* returns -1 on error */
-int
-hex2int(char *b)
-{
-	int i;
-	int n = 0;
-	int m;
-
-	for (i = 0, m = 1; i < 2; i++, m--) {
-		if (isxdigit(*(b+i))) {
-			if (*(b+i) <= '9')
-				n |= (*(b+i) & 0x0f) << (4*m);
-			else
-				n |= ((*(b+i) & 0x0f) + 9) << (4*m);
-		} else
-			return -1;
-	}
-	return n;
-}
-
diff --git a/fcoemon_utils.h b/fcoemon_utils.h
deleted file mode 100644
index 35ab19e..0000000
--- a/fcoemon_utils.h
+++ /dev/null
@@ -1,268 +0,0 @@
-/*
- * 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 _FCOEMON_UTILS_H_
-#define _FCOEMON_UTILS_H_
-
-#include <sys/types.h>
-#include <sys/time.h>
-#include <sys/socket.h>
-#include <sys/queue.h>
-#include <malloc.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <time.h>
-#include <stdarg.h>
-#include <ctype.h>
-#include <time.h>
-#include <unistd.h>
-#include <signal.h>
-#include <errno.h>
-#include <syslog.h>
-
-#include "fc_types.h"
-
-void sa_log(const char *format, ...);
-void sa_log_err(int, const char *func, const char *format, ...);
-
-/*
- * These functions can be provided outside of libsa for those environments
- * that want to redirect them.
- */
-void sa_log_output(const char *);	/* log message */
-void sa_log_abort(const char *);	/* log message and abort */
-
-#define __SA_STRING(x)  #x
-
-/*
- * Logging options.
- */
-#define SA_LOGF_TIME    0x0001      /* include timestamp in message */
-#define SA_LOGF_DELTA   0x0002      /* include time since last message */
-
-extern u_int sa_log_flags;          /* timestamp and other option flags */
-extern int sa_log_time_delta_min;   /* minimum diff to print in millisec */
-extern char *sa_log_prefix;         /* string to print before any message */
-
-extern void assert_failed(const char *s, ...)
-    __attribute__ ((format(printf, 1, 2)));
-
-#ifndef UNLIKELY
-#define UNLIKELY(_x) (_x)
-#endif /* UNLIKELY */
-
-/*
- * ASSERT macros
- *
- * ASSERT(expr) - this calls assert_failed() if expr is false.  This variant
- * is not present in production code or if DEBUG_ASSERTS is not defined.
- * Be careful not to rely on expr being evaluated.
- */
-#if defined(DEBUG_ASSERTS)
-#define ASSERT(_x) do {							\
-		if (UNLIKELY(!(_x))) {					\
-			assert_failed("ASSERT FAILED (%s) @ %s:%d\n",	\
-				"" #_x, __FILE__, __LINE__);		\
-		}							\
-	} while (0)
-#else
-#define ASSERT(_x)
-#endif /* DEBUG_ASSERTS */
-
-/*
- * ASSERT_NOTIMPL(expr) - this calls assert_failed() if expr is false.
- * The implication is that the condition is not handled by the current
- * implementation, and work should be done eventually to handle this.
- */
-#define ASSERT_NOTIMPL(_x) do {						\
-		if (UNLIKELY(!(_x))) {					\
-			assert_failed("ASSERT (NOT IMPL) "		\
-				"(%s) @ %s:%d\n",			\
-				"" #_x, __FILE__, __LINE__);		\
-		}							\
-	} while (0)
-
-/*
- * ASSERT_NOTREACHED - this is the same as ASSERT_NOTIMPL(0).
- */
-#define ASSERT_NOTREACHED do {						\
-		assert_failed("ASSERT (NOT REACHED) @ %s:%d\n",		\
-			__FILE__, __LINE__);				\
-	} while (0)
-
-/*
- * ASSERT_BUG(bugno, expr).  This variant is used when a bug number has
- * been assigned to any one of the other assertion failures.  It is always
- * present in code.  It gives the bug number which helps locate
- * documentation and helps prevent duplicate bug filings.
- */
-#define ASSERT_BUG(_bugNr, _x) do {					\
-		if (UNLIKELY(!(_x))) {					\
-			assert_failed("ASSERT (BUG %d) (%s) @ %s:%d\n", \
-				(_bugNr), #_x, __FILE__, __LINE__);	\
-		}                                                       \
-	} while (0)
-
-#ifndef LIBSA_USE_DANGEROUS_ROUTINES
-#define strcpy DONT_USE_strcpy
-#define strcat DONT_USE_strcat
-#define gets   DONT_USE_gets
-#endif /* LIBSA_USE_DANGEROUS_ROUTINES */
-
-char *sa_strncpy_safe(char *dest, size_t len, const char *src, size_t src_len);
-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);
-
-/*
- * Structure for tables encoding and decoding name-value pairs such as enums.
- */
-struct sa_nameval {
-    char    *nv_name;
-    u_int   nv_val;
-};
-
-const char *sa_enum_decode(char *, size_t, const struct sa_nameval *, u_int);
-int sa_enum_encode(const struct sa_nameval *tp, const char *, u_int *);
-const char *sa_flags_decode(char *, size_t, const struct sa_nameval *, u_int);
-
-/*
- * Timer facility.
- */
-
-struct sa_timer {
-	struct sa_timer	*tm_next;
-	u_int64_t	tm_nsec;	/* relative time to event (nSec) */
-	void		(*tm_handler)(void *arg);
-	void		*tm_handler_arg;
-	struct sa_timer **timer_head;
-};
-
-
-#define SA_TIMER_UNITS  (1000 * 1000UL)	/* number of timer ticks per second */
-
-/*
- * Initialize a pre-allocated timer structure.
- */
-void sa_timer_init(struct sa_timer *, void (*handler)(void *), void *arg);
-
-/*
- * Test whether the timer is active.
- */
-static inline int sa_timer_active(struct sa_timer *tm)
-{
-	return tm->tm_nsec != 0;
-}
-
-/*
- * Allocate a timer structure.  Set handler.
- */
-struct sa_timer *sa_timer_alloc(void (*)(void *arg), void *arg);
-
-/*
- * Set timer to fire.   Delta is in microseconds from now.
- */
-void sa_timer_set(struct sa_timer *, u_long delta);
-
-/*
- * Cancel timer.
- */
-void sa_timer_cancel(struct sa_timer *);
-
-/*
- * Free (and cancel) timer.
- */
-void sa_timer_free(struct sa_timer *);
-
-
-/*
- * Handle timer checks.  Called from select loop or other periodic function.
- *
- * The struct timeval passed in indicates how much time has passed since
- * the last call, and is set before returning to the maximum amount of time
- * that should elapse before the next call.
- *
- * Returns 1 if any timer handlers were invoked, 0 otherwise.
- */
-int sa_timer_check(struct timeval *);
-
-/*
- * Get time in nanoseconds since some arbitrary time.
- */
-u_int64_t sa_timer_get(void);
-
-/*
- * Get time in seconds since some arbitrary time.
- */
-u_int sa_timer_get_secs(void);
-
-/*
- * sa_select - Server Array select facility.
- *
- * This is a thin layer to poll files with a select loop.
- */
-
-/*
- * Enter the polling loop which never exits.
- */
-int sa_select_loop(void);
-
-/*
- * Set callback for every time through the select loop.
- */
-void sa_select_set_callback(void (*)(void));
-
-/*
- * Add a callback to handle files which are ready for receive, transmit,
- * or to handle exceptions.
- */
-void sa_select_add_fd(int fd, void (*rx_handler)(void *),
-			void (*tx_handler)(void *),
-			void (*ex_handler)(void *), void *arg);
-
-/*
- * Change a single callback for a descriptor that's already been added.
- */
-void sa_select_set_rx(int fd, void (*handler)(void *));
-void sa_select_set_tx(int fd, void (*handler)(void *));
-void sa_select_set_ex(int fd, void (*handler)(void *));
-
-/*
- * Remove all callbacks for a file descriptor.
- */
-void sa_select_rem_fd(int fd);
-
-/*
- * Cause select loop to return.
- */
-void sa_select_exit(void);
-
-/*
- * Convert 48-bit IEEE MAC address to 64-bit FC WWN.
- */
-extern fc_wwn_t
-fc_wwn_from_mac(u_int64_t, u_int32_t scheme, u_int32_t port);
-
-extern int hex2int(char *b);
-
-int use_syslog;
-void enable_syslog(int);
-
-#endif /* _FCOEMON_UTILS_H_ */
diff --git a/fipvlan.c b/fipvlan.c
index cc8a785..5b428e2 100644
--- a/fipvlan.c
+++ b/fipvlan.c
@@ -22,6 +22,7 @@
 #include <stdio.h>
 #include <stdarg.h>
 #include <stdint.h>
+#include <stdbool.h>
 #include <unistd.h>
 #include <errno.h>
 #include <getopt.h>
@@ -29,6 +30,7 @@
 #include <signal.h>
 #include <sys/ioctl.h>
 #include <sys/socket.h>
+#include <sys/queue.h>
 #include <net/if.h>
 #include <net/if_arp.h>
 #include <net/ethernet.h>
@@ -36,103 +38,99 @@
 #include <arpa/inet.h>
 #include <linux/netlink.h>
 #include <linux/rtnetlink.h>
+
+#include <sys/stat.h>
+#include <fcntl.h>
+
 #include "fcoe_utils_version.h"
 #include "fip.h"
-#include "log.h"
-#include "list.h"
+#include "fcoemon_utils.h"
+#include "fcoe_utils.h"
+#include "rtnetlink.h"
 
 #define ARRAY_SIZE(a)	(sizeof(a) / sizeof((a)[0]))
 
+#define FIP_LOG(...)		sa_log(__VA_ARGS__)
+#define FIP_LOG_ERR(error, ...)	sa_log_err(error, __func__, __VA_ARGS__)
+#define FIP_LOG_ERRNO(...)	sa_log_err(errno, __func__, __VA_ARGS__)
+#define FIP_LOG_DBG(...)	sa_log_debug(__VA_ARGS__)
+
 /* global configuration */
 
+struct {
+	char **namev;
+	int namec;
+	bool automode;
+	bool create;
+	bool start;
+} config = {
+	.namev = NULL,
+	.namec = 0,
+	.automode = false,
+	.create = false,
+};
+
 char *exe;
 
+TAILQ_HEAD(iff_list_head, iff);
+
 struct iff {
 	int ifindex;
-	char *ifname;
+	int iflink;
+	char ifname[IFNAMSIZ];
 	unsigned char mac_addr[ETHER_ADDR_LEN];
-	struct list_head list;
+	bool is_vlan;
+	short int vid;
+	TAILQ_ENTRY(iff) list_node;
+	struct iff_list_head vlans;
 };
 
-LIST_HEAD(interfaces);
+struct iff_list_head interfaces = TAILQ_HEAD_INITIALIZER(interfaces);
+
+TAILQ_HEAD(fcf_list_head, fcf);
 
 struct fcf {
-	struct iff *interface;
+	int ifindex;
 	uint16_t vlan;
 	unsigned char mac_addr[ETHER_ADDR_LEN];
-	struct list_head list;
+	TAILQ_ENTRY(fcf) list_node;
 };
 
-LIST_HEAD(fcfs);
+struct fcf_list_head fcfs = TAILQ_HEAD_INITIALIZER(fcfs);
 
-/**
- * packet_socket - create a packet socket bound to the FIP ethertype
- */
-int packet_socket(void)
+struct iff *lookup_iff(int ifindex, char *ifname)
 {
-	int s;
+	struct iff *iff;
 
-	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");
+	if (!ifindex && !ifname)
+		return NULL;
 
-	return s;
+	TAILQ_FOREACH(iff, &interfaces, list_node)
+		if ((!ifindex || ifindex == iff->ifindex) &&
+		    (!ifname  || strcmp(ifname, iff->ifname) == 0))
+			return iff;
+	return NULL;
 }
 
-/**
- * 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 iff *lookup_vlan(int ifindex, short int vid)
 {
-	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");
+	struct iff *real_dev, *vlan;
+	TAILQ_FOREACH(real_dev, &interfaces, list_node)
+		if (real_dev->ifindex == ifindex)
+			TAILQ_FOREACH(vlan, &real_dev->vlans, list_node)
+				if (vlan->vid == vid)
+					return vlan;
+	return NULL;
+}
 
-	return rc;
+struct iff *find_vlan_real_dev(struct iff *vlan)
+{
+	struct iff *real_dev;
+	TAILQ_FOREACH(real_dev, &interfaces, list_node) {
+		if (real_dev->ifindex == vlan->iflink)
+			return real_dev;
+	}
+	return NULL;
 }
 
 struct fip_tlv_ptrs {
@@ -146,7 +144,7 @@ struct fip_tlv_ptrs {
 #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); \
+		FIP_LOG("bad length for TLV of type %d", (t)->tlv_type); \
 	_tlc; \
 })
 
@@ -178,7 +176,7 @@ unsigned int fip_parse_tlvs(void *ptr, int len, struct fip_tlv_ptrs *tlv_ptrs)
 			break;
 		default:
 			/* unexpected or unrecognized descriptor */
-			log_warn("unrecognized TLV type %d", tlv->tlv_type);
+			FIP_LOG("unrecognized TLV type %d", tlv->tlv_type);
 			break;
 		}
 		len -= tlv->tlv_len;
@@ -190,180 +188,62 @@ unsigned int fip_parse_tlvs(void *ptr, int len, struct fip_tlv_ptrs *tlv_ptrs)
 /**
  * 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
+ * @ifindex: index of interface this was received on
  */
-int fip_recv_vlan_note(struct fiphdr *fh, ssize_t len, struct iff *iff)
+int fip_recv_vlan_note(struct fiphdr *fh, int ifindex)
 {
 	struct fip_tlv_ptrs tlvs;
 	struct fcf *fcf;
 	unsigned int bitmap, required_tlvs;
-	int desc_len;
+	int len;
 	int i;
 
-	log_debug(1, "received FIP VLAN Notification");
+	FIP_LOG_DBG("received FIP VLAN Notification");
 
-	desc_len = ntohs(fh->fip_desc_len);
-	if (len < (sizeof(*fh) + (desc_len << 2)))
-		return -1;
+	len = ntohs(fh->fip_desc_len);
 
 	required_tlvs = (1 << FIP_TLV_MAC_ADDR) | (1 << FIP_TLV_VLAN);
 
-	bitmap = fip_parse_tlvs((fh + 1), desc_len, &tlvs);
+	bitmap = fip_parse_tlvs((fh + 1), 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");
+			FIP_LOG_ERRNO("malloc failed");
 			break;
 		}
 		memset(fcf, 0, sizeof(*fcf));
-		fcf->interface = iff;
+		fcf->ifindex = ifindex;
 		fcf->vlan = ntohs(tlvs.vlan[i]->vlan);
 		memcpy(fcf->mac_addr, tlvs.mac->mac_addr, ETHER_ADDR_LEN);
-		list_add_tail(&fcf->list, &fcfs);
+		TAILQ_INSERT_TAIL(&fcfs, fcf, list_node);
 	}
 
 	return 0;
 }
 
-/**
- * fip_recv - receive from a FIP packet socket
- * @s: packet socket with data ready to be received
- */
-int fip_recv(int s)
+int fip_vlan_handler(struct fiphdr *fh, struct sockaddr_ll *sa, void *arg)
 {
-	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;
+	int rc = -1;
 
 	/* 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");
+		FIP_LOG_DBG("ignoring FIP packet, protocol %d",
+			    ntohs(fh->fip_proto));
 		return -1;
 	}
 
 	switch (fh->fip_subcode) {
 	case FIP_VLAN_NOTE:
-		fip_recv_vlan_note(fh, len, iff);
+		rc = fip_recv_vlan_note(fh, sa->sll_ifindex);
 		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;
+		FIP_LOG_DBG("ignored FIP VLAN packet with subcode %d",
+			    fh->fip_subcode);
+		break;
 	}
-
-	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;
 }
 
@@ -373,125 +253,64 @@ ssize_t send_getlink_dump(int s)
  */
 void rtnl_recv_newlink(struct nlmsghdr *nh)
 {
-	struct ifinfomsg *ifm;
-	struct rtattr *rta;
-	struct iff *iff;
-	unsigned int len;
-
-	log_debug(1, "RTM_NEWLINK");
+	struct ifinfomsg *ifm = NLMSG_DATA(nh);
+	struct rtattr *ifla[__IFLA_MAX];
+	struct rtattr *linkinfo[__IFLA_INFO_MAX];
+	struct rtattr *vlan[__IFLA_VLAN_MAX];
+	struct iff *iff, *real_dev;
 
-	ifm = NLMSG_DATA(nh);
-	log_debug(1, "ifindex %d, type %d", ifm->ifi_index, ifm->ifi_type);
+	FIP_LOG_DBG("RTM_NEWLINK: 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");
+		FIP_LOG_ERRNO("malloc failed");
 		return;
 	}
 	memset(iff, 0, sizeof(*iff));
+	TAILQ_INIT(&iff->vlans);
 
-	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);
-}
+	parse_ifinfo(ifla, nh);
 
-#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;
+	iff->ifindex = ifm->ifi_index;
+	if (ifla[IFLA_LINK])
+		iff->iflink = *(int *)RTA_DATA(ifla[IFLA_LINK]);
+	else
+		iff->iflink = iff->ifindex;
+	memcpy(iff->mac_addr, RTA_DATA(ifla[IFLA_ADDRESS]), ETHER_ADDR_LEN);
+	strncpy(iff->ifname, RTA_DATA(ifla[IFLA_IFNAME]), IFNAMSIZ);
+
+	if (ifla[IFLA_LINKINFO]) {
+		parse_linkinfo(linkinfo, ifla[IFLA_LINKINFO]);
+		if (linkinfo[IFLA_INFO_KIND] &&
+		    !strcmp(RTA_DATA(linkinfo[IFLA_INFO_KIND]), "vlan")) {
+			iff->is_vlan = true;
+			parse_vlaninfo(vlan, linkinfo[IFLA_INFO_DATA]);
+			iff->vid = *(int *)RTA_DATA(vlan[IFLA_VLAN_ID]);
+			real_dev = find_vlan_real_dev(iff);
+			if (!real_dev) {
+				FIP_LOG_ERR(ENODEV, "VLAN found without parent");
+				return;
+			}
+			TAILQ_INSERT_TAIL(&real_dev->vlans, iff, list_node);
+			return;
 		}
-		if (!(nh->nlmsg_flags & NLM_F_MULTI))
-			break;
 	}
-	return rc;
+	TAILQ_INSERT_TAIL(&interfaces, iff, list_node);
 }
 
 /* command line arguments */
 
-#define GETOPT_STR "ahv"
+#define GETOPT_STR "acshv"
 
 static const struct option long_options[] = {
 	{ "auto", no_argument, NULL, 'a' },
+	{ "create", no_argument, NULL, 'c' },
+	{ "start", no_argument, NULL, 's' },
 	{ "help", no_argument, NULL, 'h' },
 	{ "version", no_argument, NULL, 'v' },
 	{ NULL, 0, NULL, 0 }
@@ -503,6 +322,8 @@ static void help(int status)
 "Usage: %s [ options ] [ network interfaces ]\n"
 "Options:\n"
 "  -a, --auto           Auto select Ethernet interfaces\n"
+"  -c, --create		Create system VLAN devices\n"
+"  -s, --start		Start FCoE login automatically\n"
 "  -h, --help           Display this help and exit\n"
 "  -v, --version        Display version information and exit\n",
 	exe);
@@ -510,15 +331,9 @@ static void help(int status)
 	exit(status);
 }
 
-/* array of interface names to use */
-char **namev;
-/* length of namev */
-int namec;
-
-int parse_cmdline(int argc, char **argv)
+void parse_cmdline(int argc, char **argv)
 {
 	char c;
-	int automode = 0;
 
 	while (1) {
 		c = getopt_long(argc, argv, GETOPT_STR, long_options, NULL);
@@ -526,7 +341,13 @@ int parse_cmdline(int argc, char **argv)
 			break;
 		switch (c) {
 		case 'a':
-			automode = 1;
+			config.automode = true;
+			break;
+		case 'c':
+			config.create = true;
+			break;
+		case 's':
+			config.start = true;
 			break;
 		case 'h':
 			help(0);
@@ -542,137 +363,174 @@ int parse_cmdline(int argc, char **argv)
 		}
 	}
 
-	if ((optind == argc) && (!automode))
+	if ((optind == argc) && (!config.automode))
 		help(1);
 
-	namev = &argv[optind];
-	namec = argc - optind;
-	return automode;
+	config.namev = &argv[optind];
+	config.namec = argc - optind;
 }
 
-/* exit after waiting 2 seconds without receiving anything */
-#define TIMEOUT 2000
+int rtnl_listener_handler(struct nlmsghdr *nh, void *arg)
+{
+	switch (nh->nlmsg_type) {
+	case RTM_NEWLINK:
+		rtnl_recv_newlink(nh);
+		return 0;
+	}
+	return -1;
+}
 
-int autodetect()
+void create_missing_vlans()
 {
-	struct pollfd pfd[1];
-	int ns;
+	struct fcf *fcf;
+	struct iff *real_dev, *vlan;
+	char vlan_name[IFNAMSIZ];
 	int rc;
 
-	ns = rtnl_socket();
-	if (ns < 0)
-		return ns;
-
-	send_getlink_dump(ns);
-	pfd[0].fd = ns;
-	pfd[0].events = POLLIN;
+	if (!config.create)
+		return;
 
-	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;
+	TAILQ_FOREACH(fcf, &fcfs, list_node) {
+		vlan = lookup_vlan(fcf->ifindex, fcf->vlan);
+		if (vlan) {
+			FIP_LOG_DBG("VLAN %s.%d already exists as %s",
+				    fcf->ifindex, fcf->vlan, vlan->ifname);
+			continue;
 		}
-		if (pfd[0].revents) {
-			rc = rtnl_recv(pfd[0].fd);
-			if (rc == 0)
-				break;
+		real_dev = lookup_iff(fcf->ifindex, NULL);
+		if (!real_dev) {
+			FIP_LOG_ERR(ENODEV, "lost device %d with discoved FCF?",
+				    fcf->ifindex);
+			continue;
 		}
-		pfd[0].revents = 0;
+		snprintf(vlan_name, IFNAMSIZ, "%s.%d-fcoe",
+			 real_dev->ifname, fcf->vlan);
+		rc = vlan_create(fcf->ifindex, fcf->vlan, vlan_name);
+		if (rc < 0)
+			printf("Failed to crate VLAN device %s\n\t%s\n",
+			       vlan_name, strerror(-rc));
+		else
+			printf("Created VLAN device %s\n", vlan_name);
+		rtnl_set_iff_up(0, vlan_name);
 	}
-	close(ns);
-	return 0;
+	printf("\n");
 }
 
-int check_interface(char *name, int ps)
+int fcoe_instance_start(char *ifname)
 {
-	struct ifreq ifr;
-	struct iff *iff;
-
-	iff = malloc(sizeof(*iff));
-	if (!iff) {
-		log_errno("malloc failed");
-		return -1;
+	int fd, rc;
+	FIP_LOG_DBG("%s on %s\n", __func__, ifname);
+	fd = open(SYSFS_FCOE "/create", O_WRONLY);
+	if (fd < 0) {
+		FIP_LOG_ERRNO("failed to open fcoe create file");
+		return fd;
 	}
-	memset(iff, 0, sizeof(*iff));
+	rc = write(fd, ifname, strlen(ifname));
+	close(fd);
+	return rc < 0 ? rc : 0;
+}
 
-	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;
+void start_fcoe()
+{
+	struct fcf *fcf;
+	struct iff *iff;
 
-	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;
+	TAILQ_FOREACH(fcf, &fcfs, list_node) {
+		iff = lookup_vlan(fcf->ifindex, fcf->vlan);
+		if (!iff) {
+			FIP_LOG_ERR(ENODEV,
+				    "Cannot start FCoE on VLAN %d, ifindex %d, "
+				    "because the VLAN device does not exist",
+				    fcf->vlan, fcf->ifindex);
+			continue;
+		}
+		printf("Starting FCoE on interface %s\n", iff->ifname);
+		fcoe_instance_start(iff->ifname);
 	}
-	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 iff *iff;
 	struct fcf *fcf;
 
-	if (list_empty(&fcfs)) {
+	if (TAILQ_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,
+	printf("%-16.16s| %-5.5s| %-17.17s\n", "interface", "VLAN", "FCF MAC");
+	printf("------------------------------------------\n");
+	TAILQ_FOREACH(fcf, &fcfs, list_node) {
+		iff = lookup_iff(fcf->ifindex, NULL);
+		printf("%-16.16s| %-5d| %2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x\n",
+			iff->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]);
 	}
+	printf("\n");
 }
 
-void recv_loop(int ps)
+/* exit after waiting 1 second without receiving anything */
+#define TIMEOUT 1000
+
+void recv_loop(int ns, int ps)
 {
-	struct pollfd pfd[1] = {
-		[0].fd = ps,
+	struct pollfd pfd[2] = {
+		[0].fd = ns,
 		[0].events = POLLIN,
+		[1].fd = ps,
+		[1].events = POLLIN,
 	};
 	int rc;
 
 	while (1) {
 		rc = poll(pfd, ARRAY_SIZE(pfd), TIMEOUT);
-		log_debug(1, "return from poll %d", rc);
+		FIP_LOG_DBG("return from poll %d", rc);
 		if (rc == 0) /* timeout */
 			break;
 		if (rc == -1) {
-			log_errno("poll error");
+			FIP_LOG_ERRNO("poll error");
 			break;
 		}
 		if (pfd[0].revents)
-			fip_recv(pfd[0].fd);
+			rtnl_recv(ns, rtnl_listener_handler, NULL);
+		if (pfd[1].revents)
+			fip_recv(ps, fip_vlan_handler, NULL);
 		pfd[0].revents = 0;
+		pfd[1].revents = 0;
 	}
 }
 
-int main(int argc, char **argv)
+void find_interfaces(int ns)
+{
+	send_getlink_dump(ns);
+	rtnl_recv(ns, rtnl_listener_handler, NULL);
+}
+
+void send_vlan_requests(int ps)
 {
-	int ps;
 	struct iff *iff;
 	int i;
-	int automode;
+
+	if (config.automode) {
+		TAILQ_FOREACH(iff, &interfaces, list_node)
+			fip_send_vlan_request(ps, iff->ifindex, iff->mac_addr);
+	} else {
+		for (i = 0; i < config.namec; i++) {
+			iff = lookup_iff(0, config.namev[i]);
+			if (!iff)
+				continue;
+			fip_send_vlan_request(ps, iff->ifindex, iff->mac_addr);
+		}
+	}
+}
+
+int main(int argc, char **argv)
+{
+	int ps, ns;
+	int rc = 0;
 
 	exe = strrchr(argv[0], '/');
 	if (exe)
@@ -680,33 +538,48 @@ int main(int argc, char **argv)
 	else
 		exe = argv[0];
 
-	automode = parse_cmdline(argc, argv);
-	log_start(exe, 0, 0);
-
-	ps = packet_socket();
+	parse_cmdline(argc, argv);
+	sa_log_prefix = exe;
+	sa_log_flags = 0;
+	enable_debug_log(0);
 
-	if (automode) {
-		autodetect();
-	} else {
-		for (i = 0; i < namec; i++)
-			check_interface(namev[i], ps);
+	ps = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_FIP));
+	if (ps < 0) {
+		rc = ps;
+		goto ps_err;
+	}
+	ns = rtnl_socket();
+	if (ns < 0) {
+		rc = ns;
+		goto ns_err;
 	}
 
-	if (list_empty(&interfaces)) {
-		log_err("no interfaces to perform discovery on");
+	find_interfaces(ns);
+
+	if (TAILQ_EMPTY(&interfaces)) {
+		FIP_LOG_ERR(ENODEV, "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);
+	send_vlan_requests(ps);
+	recv_loop(ns, ps);
 
-	recv_loop(ps);
 	print_results();
-
+	if (config.create) {
+		create_missing_vlans();
+		/*
+		 * need to listen for the RTM_NETLINK messages
+		 * about the new VLAN devices
+		 */
+		recv_loop(ns, ps);
+	}
+	if (config.start)
+		start_fcoe();
+	close(ns);
+ns_err:
 	close(ps);
-	log_stop();
-	exit(0);
+ps_err:
+	exit(rc);
 }
 
diff --git a/include/fcoe_utils.h b/include/fcoe_utils.h
new file mode 100644
index 0000000..3c43304
--- /dev/null
+++ b/include/fcoe_utils.h
@@ -0,0 +1,85 @@
+/*
+ * Copyright(c) 2010 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 _FCOE_UTILS_H_
+#define _FCOE_UTILS_H_
+
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <libgen.h>
+#include <dirent.h>
+#include <errno.h>
+
+/*
+ * Used when trying to get the interface name from the symbolic_name.
+ * Not very elegant as this code will need to change if fcoe.ko changes
+ * its version.
+ */
+#define FCOE_MODULE_VERSION "v0.1"
+#define SYMB_NAME_LEAD "fcoe " FCOE_MODULE_VERSION " over "
+
+#define MAX_STR_LEN 512
+#define MAX_PATH_LEN MAX_STR_LEN
+
+#define SYSFS_MOUNT	"/sys"
+#define SYSFS_NET	SYSFS_MOUNT "/class/net"
+#define SYSFS_FCHOST	SYSFS_MOUNT "/class/fc_host"
+#define SYSFS_FCOE	SYSFS_MOUNT "/module/fcoe/parameters"
+
+#define FCHOSTBUFLEN 64
+
+/*
+ * This macro assumes that progname has been set
+ */
+#define FCOE_LOG_ERR(fmt, args...)					\
+	do {								\
+		fprintf(stderr, "%s: " fmt, progname, ##args);		\
+	} while (0)
+
+
+enum fcoe_err {
+	NOERR = 0,    /* No error */
+	EFCOECONN,    /* FCoE connection already exists */
+	ENOFCOECONN,  /* No FCoE connection on interface */
+	EINTERR,      /* Internal error */
+	EINVALARG,    /* Invalid argument */
+	EBADNUMARGS,  /* Invalid number of arguments */
+	EIGNORE,      /* Ignore this error value */
+	ENOSYSFS,     /* sysfs is not present */
+	ENOETHDEV,    /* Not a valid Ethernet interface */
+	ENOMONCONN,   /* Not connected to fcoemon */
+	ECONNTMOUT,   /* Connection to fcoemon timed out */
+	EHBAAPIERR,   /* Error using HBAAPI/libhbalinux */
+};
+
+enum fcoe_err fcoe_validate_interface(char *ifname);
+enum fcoe_err fcoe_validate_fcoe_conn(char *ifname);
+enum fcoe_err fcoe_find_fchost(char *ifname, char *fchost, int len);
+int fcoe_checkdir(char *dir);
+int check_symbolic_name_for_interface(const char *symbolic_name,
+				      const char *ifname);
+char *get_ifname_from_symbolic_name(const char *symbolic_name);
+
+#endif /* _FCOE_UTILS_H_ */
diff --git a/include/fcoe_utils_version.h.in b/include/fcoe_utils_version.h.in
new file mode 100644
index 0000000..642f37d
--- /dev/null
+++ b/include/fcoe_utils_version.h.in
@@ -0,0 +1,6 @@
+#ifndef _FCOE_UTILS_VERSION_H_
+#define _FCOE_UTILS_VERSION_H_
+
+#define FCOE_UTILS_VERSION "@VERSION@"
+
+#endif /* _FCOE_UTILS_VERSION_H_ */
diff --git a/include/fcoemon_utils.h b/include/fcoemon_utils.h
new file mode 100644
index 0000000..8680f46
--- /dev/null
+++ b/include/fcoemon_utils.h
@@ -0,0 +1,270 @@
+/*
+ * 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 _FCOEMON_UTILS_H_
+#define _FCOEMON_UTILS_H_
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <sys/queue.h>
+#include <malloc.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+#include <stdarg.h>
+#include <ctype.h>
+#include <time.h>
+#include <unistd.h>
+#include <signal.h>
+#include <errno.h>
+#include <syslog.h>
+
+#include "fc_types.h"
+
+void sa_log(const char *format, ...);
+void sa_log_debug(const char *format, ...);
+void sa_log_err(int, const char *func, const char *format, ...);
+
+/*
+ * These functions can be provided outside of libsa for those environments
+ * that want to redirect them.
+ */
+void sa_log_output(const char *);	/* log message */
+void sa_log_abort(const char *);	/* log message and abort */
+
+#define __SA_STRING(x)  #x
+
+/*
+ * Logging options.
+ */
+#define SA_LOGF_TIME    0x0001      /* include timestamp in message */
+#define SA_LOGF_DELTA   0x0002      /* include time since last message */
+
+extern u_int sa_log_flags;          /* timestamp and other option flags */
+extern int sa_log_time_delta_min;   /* minimum diff to print in millisec */
+extern char *sa_log_prefix;         /* string to print before any message */
+
+extern void assert_failed(const char *s, ...)
+    __attribute__ ((format(printf, 1, 2)));
+
+#ifndef UNLIKELY
+#define UNLIKELY(_x) (_x)
+#endif /* UNLIKELY */
+
+/*
+ * ASSERT macros
+ *
+ * ASSERT(expr) - this calls assert_failed() if expr is false.  This variant
+ * is not present in production code or if DEBUG_ASSERTS is not defined.
+ * Be careful not to rely on expr being evaluated.
+ */
+#if defined(DEBUG_ASSERTS)
+#define ASSERT(_x) do {							\
+		if (UNLIKELY(!(_x))) {					\
+			assert_failed("ASSERT FAILED (%s) @ %s:%d\n",	\
+				"" #_x, __FILE__, __LINE__);		\
+		}							\
+	} while (0)
+#else
+#define ASSERT(_x)
+#endif /* DEBUG_ASSERTS */
+
+/*
+ * ASSERT_NOTIMPL(expr) - this calls assert_failed() if expr is false.
+ * The implication is that the condition is not handled by the current
+ * implementation, and work should be done eventually to handle this.
+ */
+#define ASSERT_NOTIMPL(_x) do {						\
+		if (UNLIKELY(!(_x))) {					\
+			assert_failed("ASSERT (NOT IMPL) "		\
+				"(%s) @ %s:%d\n",			\
+				"" #_x, __FILE__, __LINE__);		\
+		}							\
+	} while (0)
+
+/*
+ * ASSERT_NOTREACHED - this is the same as ASSERT_NOTIMPL(0).
+ */
+#define ASSERT_NOTREACHED do {						\
+		assert_failed("ASSERT (NOT REACHED) @ %s:%d\n",		\
+			__FILE__, __LINE__);				\
+	} while (0)
+
+/*
+ * ASSERT_BUG(bugno, expr).  This variant is used when a bug number has
+ * been assigned to any one of the other assertion failures.  It is always
+ * present in code.  It gives the bug number which helps locate
+ * documentation and helps prevent duplicate bug filings.
+ */
+#define ASSERT_BUG(_bugNr, _x) do {					\
+		if (UNLIKELY(!(_x))) {					\
+			assert_failed("ASSERT (BUG %d) (%s) @ %s:%d\n", \
+				(_bugNr), #_x, __FILE__, __LINE__);	\
+		}                                                       \
+	} while (0)
+
+#ifndef LIBSA_USE_DANGEROUS_ROUTINES
+#define strcpy DONT_USE_strcpy
+#define strcat DONT_USE_strcat
+#define gets   DONT_USE_gets
+#endif /* LIBSA_USE_DANGEROUS_ROUTINES */
+
+char *sa_strncpy_safe(char *dest, size_t len, const char *src, size_t src_len);
+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);
+
+/*
+ * Structure for tables encoding and decoding name-value pairs such as enums.
+ */
+struct sa_nameval {
+    char    *nv_name;
+    u_int   nv_val;
+};
+
+const char *sa_enum_decode(char *, size_t, const struct sa_nameval *, u_int);
+int sa_enum_encode(const struct sa_nameval *tp, const char *, u_int *);
+const char *sa_flags_decode(char *, size_t, const struct sa_nameval *, u_int);
+
+/*
+ * Timer facility.
+ */
+
+struct sa_timer {
+	struct sa_timer	*tm_next;
+	u_int64_t	tm_nsec;	/* relative time to event (nSec) */
+	void		(*tm_handler)(void *arg);
+	void		*tm_handler_arg;
+	struct sa_timer **timer_head;
+};
+
+
+#define SA_TIMER_UNITS  (1000 * 1000UL)	/* number of timer ticks per second */
+
+/*
+ * Initialize a pre-allocated timer structure.
+ */
+void sa_timer_init(struct sa_timer *, void (*handler)(void *), void *arg);
+
+/*
+ * Test whether the timer is active.
+ */
+static inline int sa_timer_active(struct sa_timer *tm)
+{
+	return tm->tm_nsec != 0;
+}
+
+/*
+ * Allocate a timer structure.  Set handler.
+ */
+struct sa_timer *sa_timer_alloc(void (*)(void *arg), void *arg);
+
+/*
+ * Set timer to fire.   Delta is in microseconds from now.
+ */
+void sa_timer_set(struct sa_timer *, u_long delta);
+
+/*
+ * Cancel timer.
+ */
+void sa_timer_cancel(struct sa_timer *);
+
+/*
+ * Free (and cancel) timer.
+ */
+void sa_timer_free(struct sa_timer *);
+
+
+/*
+ * Handle timer checks.  Called from select loop or other periodic function.
+ *
+ * The struct timeval passed in indicates how much time has passed since
+ * the last call, and is set before returning to the maximum amount of time
+ * that should elapse before the next call.
+ *
+ * Returns 1 if any timer handlers were invoked, 0 otherwise.
+ */
+int sa_timer_check(struct timeval *);
+
+/*
+ * Get time in nanoseconds since some arbitrary time.
+ */
+u_int64_t sa_timer_get(void);
+
+/*
+ * Get time in seconds since some arbitrary time.
+ */
+u_int sa_timer_get_secs(void);
+
+/*
+ * sa_select - Server Array select facility.
+ *
+ * This is a thin layer to poll files with a select loop.
+ */
+
+/*
+ * Enter the polling loop which never exits.
+ */
+int sa_select_loop(void);
+
+/*
+ * Set callback for every time through the select loop.
+ */
+void sa_select_set_callback(void (*)(void));
+
+/*
+ * Add a callback to handle files which are ready for receive, transmit,
+ * or to handle exceptions.
+ */
+void sa_select_add_fd(int fd, void (*rx_handler)(void *),
+			void (*tx_handler)(void *),
+			void (*ex_handler)(void *), void *arg);
+
+/*
+ * Change a single callback for a descriptor that's already been added.
+ */
+void sa_select_set_rx(int fd, void (*handler)(void *));
+void sa_select_set_tx(int fd, void (*handler)(void *));
+void sa_select_set_ex(int fd, void (*handler)(void *));
+
+/*
+ * Remove all callbacks for a file descriptor.
+ */
+void sa_select_rem_fd(int fd);
+
+/*
+ * Cause select loop to return.
+ */
+void sa_select_exit(void);
+
+/*
+ * Convert 48-bit IEEE MAC address to 64-bit FC WWN.
+ */
+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 use_syslog;
+void enable_syslog(int);
+void enable_debug_log(int);
+
+#endif /* _FCOEMON_UTILS_H_ */
diff --git a/include/fip.h b/include/fip.h
index 91a017f..5f39763 100644
--- a/include/fip.h
+++ b/include/fip.h
@@ -20,6 +20,9 @@
 #ifndef FIP_H
 #define FIP_H
 
+#include <stdint.h>
+#include <net/ethernet.h>
+
 #define ETH_P_FCOE	0x8906
 #define ETH_P_FIP	0x8914
 
@@ -123,4 +126,26 @@ struct fip_tlv_vlan {
 	uint16_t vlan;	/* only lower 12 bits matter */
 };
 
+
+/* libutil / fip.c functionality */
+
+/* FIP message handler, passed into fip_recv */
+typedef int fip_handler(struct fiphdr *fh, struct sockaddr_ll *sa, void *arg);
+
+/**
+ * fip_recv - receive from a FIP packet socket
+ * @s: packet socket with data ready to be received
+ */
+int fip_recv(int s, fip_handler *fn, void *arg);
+
+/**
+ * fip_send_vlan_request - send a FIP VLAN request
+ * @s: ETH_P_FIP packet socket to send on
+ * @ifindex: network interface index to send on
+ * @mac: mac address of the netif
+ *
+ * Note: sends to FIP_ALL_FCF_MACS
+ */
+ssize_t fip_send_vlan_request(int s, int ifindex, unsigned char *mac);
+
 #endif /* FIP_H */
diff --git a/include/list.h b/include/list.h
deleted file mode 100644
index 35da9fd..0000000
--- a/include/list.h
+++ /dev/null
@@ -1,444 +0,0 @@
-/* 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
deleted file mode 100644
index 7a2a138..0000000
--- a/include/log.h
+++ /dev/null
@@ -1,15 +0,0 @@
-#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/include/rtnetlink.h b/include/rtnetlink.h
new file mode 100644
index 0000000..167748e
--- /dev/null
+++ b/include/rtnetlink.h
@@ -0,0 +1,63 @@
+/*
+ * Copyright(c) 2010 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 _RTNETLINK_
+#define _RTNETLINK_
+
+int rtnl_socket(void);
+typedef int rtnl_handler(struct nlmsghdr *nh, void *arg);
+int rtnl_recv(int s, rtnl_handler *fn, void *arg);
+ssize_t send_getlink_dump(int s);
+int rtnl_set_iff_up(int ifindex, char *ifname);
+int vlan_create(int ifindex, int vid, char *name);
+int rtnl_find_vlan(int ifindex, int vid, char *ifname);
+int rtnl_get_linkname(int ifindex, char *name);
+
+static inline void parse_rtattr(struct rtattr *tb[], int max, struct rtattr *rta, int len)
+{
+	memset(tb, 0, sizeof(struct rtattr *) * (max + 1));
+	while (RTA_OK(rta, len)) {
+		if (rta->rta_type <= max)
+			tb[rta->rta_type] = rta;
+		rta = RTA_NEXT(rta, len);
+	}
+}
+
+static inline void parse_nested_rtattr(struct rtattr *tb[], int max, struct rtattr *rta)
+{
+	parse_rtattr(tb, max, RTA_DATA(rta), RTA_PAYLOAD(rta));
+}
+
+static inline void parse_ifinfo(struct rtattr *tb[], struct nlmsghdr *nh)
+{
+	struct ifinfomsg *ifm = NLMSG_DATA(nh);
+	parse_rtattr(tb, IFLA_MAX, IFLA_RTA(ifm), IFLA_PAYLOAD(nh));
+}
+
+static inline void parse_linkinfo(struct rtattr *tb[], struct rtattr *linkinfo)
+{
+	parse_nested_rtattr(tb, IFLA_INFO_MAX, linkinfo);
+}
+
+static inline void parse_vlaninfo(struct rtattr *tb[], struct rtattr *vlan)
+{
+	parse_nested_rtattr(tb, IFLA_VLAN_MAX, vlan);
+}
+
+#endif /* _RTNETLINK_ */
diff --git a/lib/Makefile.am b/lib/Makefile.am
new file mode 100644
index 0000000..ebe259f
--- /dev/null
+++ b/lib/Makefile.am
@@ -0,0 +1,3 @@
+AM_CPPFLAGS = -I${top_srcdir}/include
+noinst_LIBRARIES = libutil.a
+libutil_a_SOURCES = fcoe_utils.c sa_log.c sa_select.c sa_timer.c sa_other.c fip.c rtnetlink.c
diff --git a/lib/fcoe_utils.c b/lib/fcoe_utils.c
new file mode 100644
index 0000000..506356d
--- /dev/null
+++ b/lib/fcoe_utils.c
@@ -0,0 +1,191 @@
+/*
+ * Copyright(c) 2010 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 "fcoe_utils.h"
+
+static int fcoe_sysfs_read(char *buf, int size, const char *path)
+{
+	FILE *fp;
+	int i, rc = -EINVAL;
+
+	fp = fopen(path, "r");
+	if (fp) {
+		if (fgets(buf, size, fp)) {
+			/*
+			 * Strip trailing newline by replacing
+			 * any '\r' or '\n' instances with '\0'.
+			 * It's not as elegant as it could be, but
+			 * we know that the symbolic name won't
+			 * have either of those characters until
+			 * the end of the line.
+			 */
+			for (i = 0; i < strlen(buf); i++) {
+				if (buf[i] == '\n' ||
+				    buf[i] == '\r') {
+					buf[i] = '\0';
+					break;
+				}
+			}
+			rc = 0;
+		}
+
+		fclose(fp);
+	}
+
+	return rc;
+}
+
+static int fcoe_check_fchost(const char *ifname, const char *dname)
+{
+	char buf[MAX_STR_LEN];
+	char path[MAX_PATH_LEN];
+	int rc = -EINVAL;
+
+	sprintf(path, "%s/%s/symbolic_name", SYSFS_FCHOST, dname);
+
+	if (!fcoe_sysfs_read(buf, MAX_STR_LEN, path))
+		rc = check_symbolic_name_for_interface(buf, ifname);
+
+	return rc;
+}
+
+enum fcoe_err fcoe_find_fchost(char *ifname, char *fchost, int len)
+{
+	int n, dname_len;
+	struct dirent **namelist;
+	int rc = ENOFCOECONN;
+
+	n = scandir(SYSFS_FCHOST, &namelist, 0, alphasort);
+
+	for (n-- ; n >= 0 ; n--) {
+		if (rc) {
+			/* check symbolic name */
+			if (!fcoe_check_fchost(ifname, namelist[n]->d_name)) {
+				dname_len = strnlen(namelist[n]->d_name, len);
+
+				if (len > dname_len) {
+					strncpy(fchost, namelist[n]->d_name,
+						dname_len + 1);
+					/* rc = 0 indicates found */
+					rc = NOERR;
+				} else {
+					/*
+					 * The fc_host is too large
+					 * for the buffer.
+					 */
+					rc = EINTERR;
+				}
+			}
+		}
+		free(namelist[n]);
+
+	}
+	free(namelist);
+
+	return rc;
+}
+
+enum fcoe_err fcoe_validate_interface(char *ifname)
+{
+	enum fcoe_err rc = NOERR;
+	char path[MAX_PATH_LEN];
+
+
+	if (!strlen(ifname))
+		rc = ENOETHDEV;
+
+	/*
+	 * TODO: Is there a better way to check if the
+	 * interface name is correct?
+	 */
+	sprintf(path, "%s/%s", SYSFS_NET, ifname);
+	if (!rc && fcoe_checkdir(path))
+		rc = ENOETHDEV;
+
+	return rc;
+}
+
+/*
+ * Validate an existing instance for an FC interface
+ */
+enum fcoe_err fcoe_validate_fcoe_conn(char *ifname)
+{
+	char fchost[FCHOSTBUFLEN];
+	enum fcoe_err rc = NOERR;
+
+	rc = fcoe_validate_interface(ifname);
+
+	if (!rc)
+		rc = fcoe_find_fchost(ifname, fchost, FCHOSTBUFLEN);
+
+	return rc;
+}
+
+/*
+ * Open and close to check if directory exists
+ */
+int fcoe_checkdir(char *dir)
+{
+	DIR *d = NULL;
+
+	if (!dir)
+		return -EINVAL;
+	/* check if we have sysfs */
+	d = opendir(dir);
+	if (!d)
+		return -EINVAL;
+	closedir(d);
+	return 0;
+}
+
+char *get_ifname_from_symbolic_name(const char *symbolic_name)
+{
+	int symbolic_name_len = strlen(symbolic_name);
+	int lead_len = strlen(SYMB_NAME_LEAD);
+
+	if (lead_len < symbolic_name_len)
+		return (char *)(symbolic_name + lead_len);
+
+       return NULL;
+}
+
+int check_symbolic_name_for_interface(const char *symbolic_name,
+				      const char *ifname)
+{
+	int rc = -EINVAL;
+	char *symb;
+
+	if (!ifname)
+		return rc;
+
+	symb = get_ifname_from_symbolic_name(symbolic_name);
+
+	/*
+	 * It's important to use the length of the ifname
+	 * from the symbolic_name here. If the ifname length
+	 * were used then if the user passed in a substring
+	 * of the the interface name it would match because
+	 * we'd only be looking for the first few characters,
+	 * not the whole string.
+	 */
+	if (symb && !strncmp(ifname, symb, strlen(symb)))
+		rc = 0;
+
+	return rc;
+}
diff --git a/lib/fip.c b/lib/fip.c
new file mode 100644
index 0000000..a5430c9
--- /dev/null
+++ b/lib/fip.c
@@ -0,0 +1,155 @@
+/*
+ * Copyright(c) 2010 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
+ */
+
+/* Routines for automatic FIP VLAN discovery and creation */
+/* Shared by fcoemon and fipvlan */
+
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <unistd.h>
+#include <errno.h>
+#include <getopt.h>
+#include <poll.h>
+#include <sys/socket.h>
+#include <sys/ioctl.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 "fcoemon_utils.h"
+
+#define FIP_LOG(...)			sa_log(__VA_ARGS__)
+#define FIP_LOG_ERR(error, ...)		sa_log_err(error, __func__, __VA_ARGS__)
+#define FIP_LOG_ERRNO(...)		sa_log_err(errno, __func__, __VA_ARGS__)
+#define FIP_LOG_DBG(...)		sa_log_debug(__VA_ARGS__)
+
+#define ARRAY_SIZE(a)	(sizeof(a) / sizeof((a)[0]))
+
+/**
+ * fip_send_vlan_request - send a FIP VLAN request
+ * @s: ETH_P_FIP packet socket to send on
+ * @ifindex: network interface index to send on
+ * @mac: mac address of the sending network interface
+ *
+ * Note: sends to FIP_ALL_FCF_MACS
+ */
+ssize_t fip_send_vlan_request(int s, int ifindex, unsigned char *mac)
+{
+	struct sockaddr_ll sa = {
+		.sll_family = AF_PACKET,
+		.sll_protocol = htons(ETH_P_FIP),
+		.sll_ifindex = 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, mac, ETHER_ADDR_LEN);
+
+	FIP_LOG_DBG("sending FIP VLAN request");
+	rc = sendmsg(s, &msg, 0);
+	if (rc < 0) {
+		rc = -errno;
+		FIP_LOG_ERRNO("sendmsg error");
+	}
+	return rc;
+}
+
+/**
+ * fip_recv - receive from a FIP packet socket
+ * @s: packet socket with data ready to be received
+ * @fn: FIP receive callback to process the payload
+ * @arg: argument to pass through to @fn
+ */
+int fip_recv(int s, fip_handler *fn, void *arg)
+{
+	char buf[4096];
+	struct sockaddr_ll sa;
+	struct iovec iov[] = {
+		{ .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 fiphdr *fh;
+	ssize_t len, desc_len;
+
+	FIP_LOG_DBG("%s", __func__);
+
+	len = recvmsg(s, &msg, 0);
+	if (len < 0) {
+		FIP_LOG_ERRNO("packet socket recv error");
+		return len;
+	}
+
+	if (len < sizeof(*fh)) {
+		FIP_LOG_ERR(EINVAL, "received packed smaller that FIP header");
+		return -1;
+	}
+
+	fh = (struct fiphdr *) buf;
+
+	desc_len = ntohs(fh->fip_desc_len);
+	if (len < (sizeof(*fh) + (desc_len << 2))) {
+		FIP_LOG_ERR(EINVAL, "received data less that FIP descriptor");
+		return -1;
+	}
+
+	if (fn)
+		return fn(fh, &sa, arg);
+	return 0;
+}
diff --git a/lib/rtnetlink.c b/lib/rtnetlink.c
new file mode 100644
index 0000000..b7a32f2
--- /dev/null
+++ b/lib/rtnetlink.c
@@ -0,0 +1,429 @@
+/*
+ * Copyright(c) 2010 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 <stdbool.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 <sys/queue.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 "rtnetlink.h"
+#include "fcoemon_utils.h"
+
+#define RTNL_LOG(...)		sa_log(__VA_ARGS__)
+#define RTNL_LOG_ERR(error, ...)	sa_log_err(error, __func__, __VA_ARGS__)
+#define RTNL_LOG_ERRNO(...)	sa_log_err(errno, __func__, __VA_ARGS__)
+#define RTNL_LOG_DBG(...)	sa_log_debug(__VA_ARGS__)
+
+/**
+ * rtnl_socket - create and bind a routing netlink socket
+ */
+int rtnl_socket(void)
+{
+	struct sockaddr_nl sa = {
+		.nl_family = AF_NETLINK,
+		.nl_groups = RTMGRP_LINK,
+	};
+	int s;
+	int rc;
+
+	RTNL_LOG_DBG("creating netlink socket");
+	s = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
+	if (s < 0) {
+		RTNL_LOG_ERRNO("netlink socket error");
+		return s;
+	}
+
+	rc = bind(s, (struct sockaddr *) &sa, sizeof(sa));
+	if (rc < 0) {
+		RTNL_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 {
+		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,
+		},
+		.ifm = {
+			.ifi_type = ARPHRD_ETHER,
+		},
+	};
+	int rc;
+
+	RTNL_LOG_DBG("sending RTM_GETLINK dump request");
+	rc = send(s, &req, req.nh.nlmsg_len, 0);
+	if (rc < 0)
+		RTNL_LOG_ERRNO("netlink sendmsg error");
+
+	return rc;
+}
+
+#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, rtnl_handler *fn, void *arg)
+{
+	char buf[8192];
+	struct nlmsghdr *nh;
+	int len;
+	int rc = 0;
+	bool more = false;
+
+	RTNL_LOG_DBG("%s", __func__);
+more:
+	len = recv(s, buf, sizeof(buf), 0);
+	if (len < 0) {
+		RTNL_LOG_ERRNO("netlink recvmsg error");
+		return len;
+	}
+
+	for (nh = NLMSG(buf); NLMSG_OK(nh, len); nh = NLMSG_NEXT(nh, len)) {
+		if (nh->nlmsg_flags & NLM_F_MULTI)
+			more = true;
+
+		switch (nh->nlmsg_type) {
+		case NLMSG_NOOP:
+			RTNL_LOG_DBG("NLMSG_NOOP");
+			break;
+		case NLMSG_ERROR:
+			rc = ((struct nlmsgerr *)NLMSG_DATA(nh))->error;
+			RTNL_LOG_DBG("NLMSG_ERROR (%d) %s", rc, strerror(-rc));
+			break;
+		case NLMSG_DONE:
+			more = false;
+			RTNL_LOG_DBG("NLMSG_DONE");
+			break;
+		default:
+			if (!fn || fn(nh, arg) < 0)
+				RTNL_LOG("unexpected netlink message type %d",
+					 nh->nlmsg_type);
+			break;
+		}
+	}
+	if (more)
+		goto more;
+	return rc;
+}
+
+#define NLMSG_TAIL(nmsg) \
+	((struct rtattr *)(((void *)(nmsg)) + NLMSG_ALIGN((nmsg)->nlmsg_len)))
+
+static void add_rtattr(struct nlmsghdr *n, int type, const void *data, int alen)
+{
+	struct rtattr *rta = NLMSG_TAIL(n);
+	int len = RTA_LENGTH(alen);
+
+	rta->rta_type = type;
+	rta->rta_len = len;
+	memcpy(RTA_DATA(rta), data, alen);
+	n->nlmsg_len = NLMSG_ALIGN(n->nlmsg_len) + RTA_ALIGN(len);
+}
+
+static struct rtattr *add_rtattr_nest(struct nlmsghdr *n, int type)
+{
+	struct rtattr *nest = NLMSG_TAIL(n);
+
+	add_rtattr(n, type, NULL, 0);
+	return nest;
+}
+
+static void end_rtattr_nest(struct nlmsghdr *n, struct rtattr *nest)
+{
+	nest->rta_len = (void *)NLMSG_TAIL(n) - (void *)nest;
+}
+
+static ssize_t rtnl_send_set_iff_up(int s, int ifindex, char *ifname)
+{
+	struct {
+		struct nlmsghdr nh;
+		struct ifinfomsg ifm;
+		char attrbuf[RTA_SPACE(IFNAMSIZ)];
+	} req = {
+		.nh = {
+			.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)),
+			.nlmsg_type = RTM_SETLINK,
+			.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK,
+		},
+		.ifm = {
+			.ifi_index = ifindex,
+			.ifi_flags = IFF_UP,
+			.ifi_change = IFF_UP,
+		},
+	};
+	int rc;
+
+	if (ifname)
+		add_rtattr(&req.nh, IFLA_IFNAME, ifname, strlen(ifname));
+
+	RTNL_LOG_DBG("sending RTM_SETLINK request");
+	rc = send(s, &req, req.nh.nlmsg_len, 0);
+	if (rc < 0)
+		RTNL_LOG_ERRNO("netlink send error");
+
+	return rc;
+}
+
+int rtnl_set_iff_up(int ifindex, char *ifname)
+{
+	int s;
+	int rc;
+
+	s = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE);
+	if (s < 0)
+		return s;
+	rc = rtnl_send_set_iff_up(s, ifindex, ifname);
+	if (rc < 0)
+		goto out;
+	rc = rtnl_recv(s, NULL, NULL);
+out:
+	close(s);
+	return rc;
+}
+
+static ssize_t rtnl_send_vlan_newlink(int s, int ifindex, int vid, char *name)
+{
+	struct {
+		struct nlmsghdr nh;
+		struct ifinfomsg ifm;
+		char attrbuf[1024];
+	} req = {
+		.nh = {
+			.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)),
+			.nlmsg_type = RTM_NEWLINK,
+			.nlmsg_flags = NLM_F_REQUEST | NLM_F_CREATE |
+				       NLM_F_EXCL | NLM_F_ACK,
+		},
+	};
+	struct rtattr *linkinfo, *data;
+	int rc;
+
+	add_rtattr(&req.nh, IFLA_LINK, &ifindex, 4);
+	add_rtattr(&req.nh, IFLA_IFNAME, name, strlen(name));
+	linkinfo = add_rtattr_nest(&req.nh, IFLA_LINKINFO);
+	add_rtattr(&req.nh, IFLA_INFO_KIND, "vlan", strlen("vlan"));
+	data = add_rtattr_nest(&req.nh, IFLA_INFO_DATA);
+	add_rtattr(&req.nh, IFLA_VLAN_ID, &vid, 2);
+	end_rtattr_nest(&req.nh, data);
+	end_rtattr_nest(&req.nh, linkinfo);
+
+	RTNL_LOG_DBG("sending RTM_NEWLINK request");
+	rc = send(s, &req, req.nh.nlmsg_len, 0);
+	if (rc < 0)
+		RTNL_LOG_ERRNO("netlink send error");
+
+	return rc;
+}
+
+int vlan_create(int ifindex, int vid, char *name)
+{
+	int s;
+	int rc;
+
+	s = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE);
+	if (s < 0)
+		return s;
+
+	rc = rtnl_send_vlan_newlink(s, ifindex, vid, name);
+	if (rc < 0)
+		goto out;
+	rc = rtnl_recv(s, NULL, NULL);
+out:
+	close(s);
+	return rc;
+}
+
+static ssize_t rtnl_send_getlink(int s, int ifindex, char *name)
+{
+	struct {
+		struct nlmsghdr nh;
+		struct ifinfomsg ifm;
+		char attrbuf[RTA_SPACE(IFNAMSIZ)];
+	} req = {
+		.nh = {
+			.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)),
+			.nlmsg_type = RTM_GETLINK,
+			.nlmsg_flags = NLM_F_REQUEST,
+		},
+		.ifm = {
+			.ifi_family = AF_UNSPEC,
+			.ifi_index = ifindex,
+		},
+	};
+	int rc;
+
+	if (!ifindex && !name)
+		return -1;
+
+	if (name)
+		add_rtattr(&req.nh, IFLA_IFNAME, name, strlen(name));
+
+	RTNL_LOG_DBG("sending RTM_GETLINK");
+	rc = send(s, &req, req.nh.nlmsg_len, 0);
+	if (rc < 0)
+		RTNL_LOG_ERRNO("netlink send error");
+
+	return rc;
+}
+
+static int rtnl_getlinkname_handler(struct nlmsghdr *nh, void *arg)
+{
+	char *name = arg;
+	struct ifinfomsg *ifm;
+	struct rtattr *ifla[__IFLA_MAX];
+
+	switch (nh->nlmsg_type) {
+	case RTM_NEWLINK:
+		ifm = NLMSG_DATA(nh);
+		parse_ifinfo(ifla, nh);
+		strncpy(name, RTA_DATA(ifla[IFLA_IFNAME]), IFNAMSIZ);
+		return 0;
+	}
+	return -1;
+}
+
+int rtnl_get_linkname(int ifindex, char *name)
+{
+	int s;
+	int rc;
+
+	s = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE);
+	if (s < 0)
+		return s;
+	rc = rtnl_send_getlink(s, ifindex, NULL);
+	if (rc < 0)
+		return rc;
+	rc = rtnl_recv(s, rtnl_getlinkname_handler, name);
+	if (rc < 0)
+		goto out;
+out:
+	close(s);
+	return rc;
+}
+
+struct vlan_identifier {
+	int ifindex;
+	int vid;
+	int found;
+	unsigned char ifname[IFNAMSIZ];
+};
+
+static int rtnl_find_vlan_handler(struct nlmsghdr *nh, void *arg)
+{
+	struct vlan_identifier *vlan = arg;
+	struct ifinfomsg *ifm;
+	struct rtattr *ifla[__IFLA_MAX];
+	struct rtattr *linkinfo[__IFLA_INFO_MAX];
+	struct rtattr *vlaninfo[__IFLA_VLAN_MAX];
+
+	switch (nh->nlmsg_type) {
+	case RTM_NEWLINK:
+		ifm = NLMSG_DATA(nh);
+		parse_ifinfo(ifla, nh);
+		if (!ifla[IFLA_LINK])
+			break;
+		if (vlan->ifindex != *(int *)RTA_DATA(ifla[IFLA_LINK]))
+			break;
+		if (!ifla[IFLA_LINKINFO])
+			break;
+		parse_linkinfo(linkinfo, ifla[IFLA_LINKINFO]);
+		if (!linkinfo[IFLA_INFO_KIND])
+			break;
+		if (strcmp(RTA_DATA(linkinfo[IFLA_INFO_KIND]), "vlan"))
+			break;
+		if (!linkinfo[IFLA_INFO_DATA])
+			break;
+		parse_vlaninfo(vlaninfo, linkinfo[IFLA_INFO_DATA]);
+		if (!vlaninfo[IFLA_VLAN_ID])
+			break;
+		if (vlan->vid != *(int *)RTA_DATA(vlaninfo[IFLA_VLAN_ID]))
+			break;
+		if (!ifla[IFLA_IFNAME])
+			break;
+		vlan->found = 1;
+		memcpy(vlan->ifname, RTA_DATA(ifla[IFLA_IFNAME]), IFNAMSIZ);
+	}
+	return 0;
+}
+
+int rtnl_find_vlan(int ifindex, int vid, char *ifname)
+{
+	int s;
+	int rc;
+	struct vlan_identifier vlan = {
+		.ifindex = ifindex,
+		.vid = vid,
+		.found = 0,
+	};
+
+	s = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE);
+	if (s < 0)
+		return s;
+	rc = send_getlink_dump(s);
+	if (rc < 0)
+		goto out;
+	rc = rtnl_recv(s, rtnl_find_vlan_handler, &vlan);
+	if (rc < 0)
+		goto out;
+	if (vlan.found) {
+		memcpy(ifname, vlan.ifname, IFNAMSIZ);
+		rc = 0;
+	} else {
+		rc = -ENODEV;
+	}
+out:
+	close(s);
+	return rc;
+}
diff --git a/lib/sa_log.c b/lib/sa_log.c
new file mode 100644
index 0000000..91c256f
--- /dev/null
+++ b/lib/sa_log.c
@@ -0,0 +1,256 @@
+/*
+ * 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 "fcoemon_utils.h"
+#include "net_types.h"
+#include "fc_types.h"
+
+int use_syslog;
+static int debug;
+
+/*
+ * 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;
+}
+
+void enable_debug_log(int enable)
+{
+	debug = enable;
+}
+
+/*
+ * 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
+ */
+void
+sa_log(const char *format, ...)
+{
+	va_list arg;
+
+	va_start(arg, format);
+	sa_log_va(NULL, format, arg);
+	va_end(arg);
+}
+
+/*
+ * debug log, controlled by static debug flag
+ */
+void
+sa_log_debug(const char *format, ...)
+{
+	va_list arg;
+
+	if (!debug)
+		return;
+
+	va_start(arg, format);
+	sa_log_va(NULL, format, arg);
+	va_end(arg);
+}
+
+/*
+ * log with error number.
+ */
+void
+sa_log_err(int error, const char *func, const char *format, ...)
+{
+	va_list arg;
+	char buf[SA_LOG_BUF_LEN];
+
+	if (func)
+		sa_log("%s: error %d %s", func, error,
+		       strerror_r(error, buf, sizeof(buf)));
+	else
+		sa_log("error %d %s", error,
+		       strerror_r(error, buf, sizeof(buf)));
+	va_start(arg, format);
+	sa_log_va(func, format, arg);
+	va_end(arg);
+}
+
+/*
+ * 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 */
+
+/*
+ * Assert failures.
+ */
+void
+assert_failed(const char *format, ...)
+{
+	va_list arg;
+	char buf[SA_LOG_BUF_LEN];
+
+	va_start(arg, format);
+	vsnprintf(buf, sizeof(buf), format, arg);
+	va_end(arg);
+	sa_log_abort(buf);
+}
+
+/*
+ * Log options.
+ * These may be set directly by callers.
+ */
+u_int sa_log_flags;                     /* timestamp and other option flags */
+int sa_log_time_delta_min = 1;          /* minimum diff to print in millisec */
+char *sa_log_prefix;                    /* string to print before any message */
+
+void
+sa_log_set_option(u_int flags)
+{
+	sa_log_flags = flags;
+}
+
+/*
+ * Put timestamp on front of each log line, as controlled by tunables above.
+ */
+static void
+sa_log_timestamp(void)
+{
+	static struct timeval tlast;
+	char ctime_buf[30];
+	struct timeval t;
+	struct timeval diff;
+
+	gettimeofday(&t, NULL);
+	if (sa_log_flags & SA_LOGF_TIME) {
+		ctime_r(&t.tv_sec, ctime_buf);
+		ctime_buf[11 + 8] = '\0';   /* trim ctime after seconds */
+		fprintf(stderr, "%s.%3.3ld ",
+			ctime_buf + 11, t.tv_usec / 1000);
+	}
+	if (sa_log_flags & SA_LOGF_DELTA) {
+		if (tlast.tv_sec == 0)
+			tlast = t;
+		timersub(&t, &tlast, &diff);
+		tlast = t;
+		if (diff.tv_sec != 0 ||
+		    diff.tv_usec >= sa_log_time_delta_min * 1000)
+			fprintf(stderr, "%4ld.%3.3ld ",
+				diff.tv_sec, diff.tv_usec / 1000);
+		else
+			fprintf(stderr, "%8s ", "");
+	}
+	if (sa_log_prefix)
+		fprintf(stderr, "%s: ", sa_log_prefix);
+}
+
+void
+sa_log_output(const char *buf)
+{
+	if (use_syslog) {
+		syslog(LOG_INFO, "%s", buf);
+		return;
+	}
+	sa_log_timestamp();
+	fprintf(stderr, "%s", buf);
+	fflush(stderr);
+}
+
+void
+sa_log_abort(const char *buf)
+{
+	sa_log_output(buf);
+	abort();
+}
+
+/*
+ * Make a printable NUL-terminated copy of the string.
+ * The source buffer might not be NUL-terminated.
+ */
+char *
+sa_strncpy_safe(char *dest, size_t len, const char *src, size_t src_len)
+{
+	char *dp = dest;
+	const char *sp = src;
+
+	while (len-- > 1 && src_len-- > 0 && *sp != '\0') {
+		*dp++ = isprint(*sp) ? *sp : (isspace(*sp) ? ' ' : '.');
+		sp++;
+	}
+	*dp = '\0';
+
+	/*
+	 * Take off trailing blanks.
+	 */
+	while (--dp >= dest && isspace(*dp))
+		*dp = '\0';
+	return dest;
+}
+
+/** sa_enum_decode(buf, len, tp, val)
+ *
+ * @param buf buffer for result (may be used or not).
+ * @param len size of buffer (at least 32 bytes recommended).
+ * @param tp pointer to table of names and values, struct sa_nameval.
+ * @param val value to be decoded into a name.
+ * @returns pointer to name string.  Unknown values are put into buffer in hex.
+ */
+const char *
+sa_enum_decode(char *buf, size_t len, const struct sa_nameval *tp, u_int val)
+{
+	for (; tp->nv_name != NULL; tp++) {
+		if (tp->nv_val == val)
+			return tp->nv_name;
+	}
+	snprintf(buf, len, "Unknown (code 0x%X)", val);
+	return buf;
+}
diff --git a/lib/sa_other.c b/lib/sa_other.c
new file mode 100644
index 0000000..af53a54
--- /dev/null
+++ b/lib/sa_other.c
@@ -0,0 +1,69 @@
+/*
+ * 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 "fcoemon_utils.h"
+#include "net_types.h"
+#include "fc_types.h"
+
+/*
+ * Convert 48-bit IEEE MAC address to 64-bit FC WWN.
+ */
+fc_wwn_t
+fc_wwn_from_mac(u_int64_t mac, u_int scheme, u_int 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_NOTREACHED;
+		break;
+	}
+	return wwn;
+}
+
+/* assumes input is pointer to two hex digits */
+/* returns -1 on error */
+int
+hex2int(char *b)
+{
+	int i;
+	int n = 0;
+	int m;
+
+	for (i = 0, m = 1; i < 2; i++, m--) {
+		if (isxdigit(*(b+i))) {
+			if (*(b+i) <= '9')
+				n |= (*(b+i) & 0x0f) << (4*m);
+			else
+				n |= ((*(b+i) & 0x0f) + 9) << (4*m);
+		} else
+			return -1;
+	}
+	return n;
+}
+
diff --git a/lib/sa_select.c b/lib/sa_select.c
new file mode 100644
index 0000000..1b18a6e
--- /dev/null
+++ b/lib/sa_select.c
@@ -0,0 +1,213 @@
+/*
+ * 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 "fcoemon_utils.h"
+#include "net_types.h"
+#include "fc_types.h"
+
+#define NFC_NFDS        64
+
+/*
+* Static module state.
+*/
+static struct sa_sel_state {
+	fd_set      ts_rx_fds;
+	fd_set      ts_tx_fds;
+	fd_set      ts_ex_fds;
+	int         ts_max_fd;
+	u_char      ts_exit;
+	struct sa_sel_fd {
+		void    (*ts_rx_handler)(void *);
+		void    (*ts_tx_handler)(void *);
+		void    (*ts_ex_handler)(void *);
+		void    *ts_handler_arg;
+	} ts_fd[NFC_NFDS];
+	void        (*ts_callback)(void);
+} sa_sel_state;
+
+int sa_select_loop(void)
+{
+	struct sa_sel_state *ss = &sa_sel_state;
+	struct sa_sel_fd *fp;
+	fd_set rx_fds;
+	fd_set tx_fds;
+	fd_set ex_fds;
+	struct timeval tval;
+	struct timeval *tvp;
+	int rv, i;
+
+	ss->ts_exit = 0;
+	while (ss->ts_exit == 0) {
+		sa_timer_check(&tval);
+		if (ss->ts_exit)
+			break;
+		if (tval.tv_sec == 0 && tval.tv_usec == 0)
+			tvp = NULL;
+		else
+			tvp = &tval;
+		rx_fds = ss->ts_rx_fds;
+		tx_fds = ss->ts_tx_fds;
+		ex_fds = ss->ts_ex_fds;
+		rv = select(ss->ts_max_fd + 1, &rx_fds, &tx_fds, &ex_fds, tvp);
+		if (rv == -1) {
+			if (errno == EINTR)
+				continue;
+			return errno;
+		}
+
+		fp = ss->ts_fd;
+		for (i = 0; rv > 0 && i <= sa_sel_state.ts_max_fd; i++, fp++) {
+			if (FD_ISSET(i, &rx_fds)) {
+				if (fp->ts_rx_handler != NULL)
+					(*fp->ts_rx_handler)
+					(fp->ts_handler_arg);
+				else
+					ASSERT(!FD_ISSET(i, &ss->ts_rx_fds));
+				--rv;
+			}
+			if (FD_ISSET(i, &tx_fds)) {
+				if (fp->ts_tx_handler != NULL)
+					(*fp->ts_tx_handler)
+					(fp->ts_handler_arg);
+				else
+					ASSERT(!FD_ISSET(i, &ss->ts_tx_fds));
+				--rv;
+			}
+			if (FD_ISSET(i, &ex_fds)) {
+				if (fp->ts_ex_handler != NULL)
+					(*fp->ts_ex_handler)
+					(fp->ts_handler_arg);
+				else
+					ASSERT(!FD_ISSET(i, &ss->ts_ex_fds));
+				--rv;
+			}
+		}
+		if (ss->ts_callback != NULL)
+			(*ss->ts_callback)();
+	}
+	return 0;
+}
+
+void
+sa_select_add_fd(int fd,
+		 void (*rx_handler)(void *),
+		 void (*tx_handler)(void *),
+		 void (*ex_handler)(void *),
+		 void *arg)
+{
+	struct sa_sel_state *ss = &sa_sel_state;
+	struct sa_sel_fd *fp;
+
+	ASSERT_NOTIMPL(fd < NFC_NFDS);
+	ASSERT(rx_handler != NULL || tx_handler != NULL || ex_handler != NULL);
+	if (ss->ts_max_fd < fd)
+		ss->ts_max_fd = fd;
+	fp = &ss->ts_fd[fd];
+	fp->ts_handler_arg = arg;
+	if (rx_handler != NULL) {
+		fp->ts_rx_handler = rx_handler;
+		FD_SET(fd, &ss->ts_rx_fds);
+	}
+	if (tx_handler != NULL) {
+		fp->ts_tx_handler = tx_handler;
+		FD_SET(fd, &ss->ts_tx_fds);
+	}
+	if (ex_handler != NULL) {
+		fp->ts_ex_handler = ex_handler;
+		FD_SET(fd, &ss->ts_ex_fds);
+	}
+}
+
+void
+sa_select_set_rx(int fd, void (*handler)(void *))
+{
+	struct sa_sel_state *ss = &sa_sel_state;
+
+	ASSERT(fd <= ss->ts_max_fd);
+	ss->ts_fd[fd].ts_rx_handler = handler;
+	if (handler != NULL)
+		FD_SET(fd, &ss->ts_rx_fds);
+	else
+		FD_CLR(fd, &ss->ts_rx_fds);
+}
+
+void
+sa_select_set_tx(int fd, void (*handler)(void *))
+{
+	struct sa_sel_state *ss = &sa_sel_state;
+
+	ASSERT(fd <= ss->ts_max_fd);
+	ss->ts_fd[fd].ts_tx_handler = handler;
+	if (handler != NULL)
+		FD_SET(fd, &ss->ts_tx_fds);
+	else
+		FD_CLR(fd, &ss->ts_tx_fds);
+}
+
+void
+sa_select_set_ex(int fd, void (*handler)(void *))
+{
+	struct sa_sel_state *ss = &sa_sel_state;
+
+	ASSERT(fd <= ss->ts_max_fd);
+	ss->ts_fd[fd].ts_ex_handler = handler;
+	if (handler != NULL)
+		FD_SET(fd, &ss->ts_ex_fds);
+	else
+		FD_CLR(fd, &ss->ts_ex_fds);
+}
+
+void
+sa_select_rem_fd(int fd)
+{
+	struct sa_sel_state *ss = &sa_sel_state;
+	struct sa_sel_fd *fp;
+
+	ASSERT_NOTIMPL(fd < NFC_NFDS);
+	FD_CLR(fd, &ss->ts_rx_fds);
+	FD_CLR(fd, &ss->ts_tx_fds);
+	FD_CLR(fd, &ss->ts_ex_fds);
+	fp = &ss->ts_fd[fd];
+	fp->ts_rx_handler = NULL;
+	fp->ts_tx_handler = NULL;
+	fp->ts_ex_handler = NULL;
+	fp->ts_handler_arg = NULL;
+}
+
+/*
+ * Set callback for every time through the select loop.
+ */
+void
+sa_select_set_callback(void (*cb)(void))
+{
+	sa_sel_state.ts_callback = cb;
+}
+
+/*
+ * Cause select loop to exit.
+ * This is invoked from a handler which wants the select loop to return
+ * after the handler is finished.  For example, during receipt of a network
+ * packet, the program may decide to clean up and exit, but in order to do
+ * this cleanly, all lower-level protocol handlers should return first.
+ */
+void
+sa_select_exit(void)
+{
+	sa_sel_state.ts_exit = 1;
+}
diff --git a/lib/sa_timer.c b/lib/sa_timer.c
new file mode 100644
index 0000000..aee213f
--- /dev/null
+++ b/lib/sa_timer.c
@@ -0,0 +1,226 @@
+/*
+ * 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 "fcoemon_utils.h"
+#include "net_types.h"
+#include "fc_types.h"
+
+#define SA_TIMER_HZ     (1000 * 1000 * 1000ULL)	/* nanoseconds per second */
+#define SA_TIMER_FUZZ   (500 * 1000ULL)	/* 500 microseconds is close enough */
+
+static struct sa_timer *sa_timer_head;	/* queue of scheduled events */
+static u_int64_t sa_timer_nsec;		/* nanoseconds since start */
+
+/*
+ * Initialize a timer structure.  Set handler.
+ */
+void
+sa_timer_init(struct sa_timer *tm, void (*handler)(void *), void *arg)
+{
+	ASSERT(handler != NULL);
+	memset(tm, 0, sizeof(*tm));
+	tm->tm_handler = handler;
+	tm->tm_handler_arg = arg;
+}
+
+/*
+ * Allocate a timer structure.  Set handler.
+ */
+struct sa_timer *
+sa_timer_alloc(void (*handler)(void *arg), void *arg)
+{
+	struct sa_timer *tm;
+
+	tm = malloc(sizeof(*tm));
+	if (tm)
+		sa_timer_init(tm, handler, arg);
+	return tm;
+}
+
+u_int64_t
+sa_timer_get(void)
+{
+	u_int64_t nsec;
+#ifndef _POSIX_TIMERS
+	struct timeval tv;
+
+	gettimeofday(&tv, NULL);	/* XXX want monotonic time, not TOD */
+	nsec = tv.tv_sec * SA_TIMER_HZ + tv.tv_usec * 1000;
+#else /* _POSIX_TIMERS */
+	struct timespec ts;
+	int rc;
+
+	rc = clock_gettime(CLOCK_MONOTONIC, &ts);
+	ASSERT_NOTIMPL(rc == 0);
+	nsec = ts.tv_sec * SA_TIMER_HZ + ts.tv_nsec;
+#endif /* _POSIX_TIMERS */
+
+#if 0 /* XXX */
+	ASSERT(nsec >= sa_timer_nsec);	/* really must be monotonic */
+#else
+	if (nsec < sa_timer_nsec)
+		sa_log("sa_timer_get: negative time lapse "
+			"old %qud new %qud diff %qd nsec\n",
+			(long long unsigned int) sa_timer_nsec,
+			(long long unsigned int) nsec,
+			(long long int) (nsec - sa_timer_nsec));
+#endif
+	sa_timer_nsec = nsec;
+	return nsec;
+}
+
+/*
+ * Get monotonic time since some arbitrary time in the past.
+ * If _POSIX_MONOTONIC_CLOCK isn't available, we'll use time of day.
+ */
+u_int
+sa_timer_get_secs(void)
+{
+	u_int sec;
+
+#ifndef _POSIX_TIMERS
+	struct timeval tv;
+
+	gettimeofday(&tv, NULL); /* XXX want monotonic time, not TOD */
+	sec = tv.tv_sec;
+#else /* _POSIX_TIMERS */
+	struct timespec ts;
+	int rc;
+
+	rc = clock_gettime(CLOCK_MONOTONIC, &ts);
+	ASSERT_NOTIMPL(rc == 0);
+	sec = ts.tv_sec;
+#endif /* _POSIX_TIMERS */
+	return sec;
+}
+
+/*
+ * Set timer to fire.   Delta is in microseconds from now.
+ */
+void
+sa_timer_set(struct sa_timer *tm, u_long delta_usec)
+{
+	struct sa_timer *cur;
+	struct sa_timer **prev;
+
+	ASSERT(delta_usec != 0);
+	ASSERT(tm->tm_handler != NULL);
+	sa_timer_cancel(tm);
+	ASSERT(sa_timer_active(tm) == 0);
+	tm->tm_nsec =
+	    sa_timer_get() + delta_usec * SA_TIMER_HZ / SA_TIMER_UNITS;
+	ASSERT(tm->tm_nsec != 0);
+
+	/*
+	 * Insert timer into sorted linked list.
+	 * Find insertion point, before cur.
+	 */
+	for (prev = &sa_timer_head;
+	     (cur = *prev) != NULL && cur->tm_nsec <= tm->tm_nsec;
+	     prev = &cur->tm_next)
+		;
+	*prev = tm;
+	tm->tm_next = cur;
+}
+
+/*
+ * Cancel timer if it is active.
+ */
+void
+sa_timer_cancel(struct sa_timer *tm)
+{
+	struct sa_timer *cur;
+	struct sa_timer **prev;
+
+	if (sa_timer_active(tm)) {
+		for (prev = &sa_timer_head; (cur = *prev) != NULL;
+		     prev = &cur->tm_next)
+			if (cur == tm) {
+				tm->tm_nsec = 0;
+				*prev = tm->tm_next;
+				break;
+			}
+		ASSERT(cur == tm);
+	}
+}
+
+/*
+ * Free (and cancel) timer.
+ */
+void
+sa_timer_free(struct sa_timer *tm)
+{
+	if (sa_timer_active(tm))
+		sa_timer_cancel(tm);
+	free(tm);
+}
+
+/*
+ * Handle timer checks.  Called from select loop or other periodic function.
+ *
+ * The struct timeval is set before returning to the maximum amount of time
+ * that should elapse before the next call.
+ *
+ * Returns 1 if any timer functions were called, 0 otherwise.
+ */
+int
+sa_timer_check(struct timeval *tv)
+{
+	u_int64_t now = 0;
+	u_int64_t next_due = 0;
+	struct sa_timer *tm;
+	int ret = 0;
+
+	/*
+	 * Remember, the list may change during the handler.
+	 */
+	for (;;) {
+		now = sa_timer_get();
+		tm = sa_timer_head;
+		if (tm == NULL) {
+			next_due = now;
+			break;
+		}
+
+		next_due = tm->tm_nsec;
+		if (next_due > now + SA_TIMER_FUZZ)
+			break;
+
+		/*
+		 * Remove this element from the list.
+		 */
+		sa_timer_head = tm->tm_next;
+		tm->tm_next = NULL;
+
+		/*
+		 * Mark cancelled and call handler.
+		 */
+		tm->tm_nsec = 0;
+		ASSERT(tm->tm_handler != NULL);
+		(*tm->tm_handler)(tm->tm_handler_arg);
+		ret = 1;
+	}
+
+	ASSERT(next_due >= now);
+	next_due -= now;
+	tv->tv_sec = (time_t) (next_due / SA_TIMER_HZ);
+	tv->tv_usec = (long) (next_due % SA_TIMER_HZ) / 1000;
+
+	return ret;
+}
diff --git a/log.c b/log.c
deleted file mode 100644
index a99ab93..0000000
--- a/log.c
+++ /dev/null
@@ -1,87 +0,0 @@
-/*
- * 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