[Open-FCoE] [PATCH] fcoe-utils: add a FIP VLAN discovery tool

Chris Leech christopher.leech at intel.com
Wed Jul 8 16:42:09 UTC 2009


This adds a FIP VLAN discovery tool.  It finds available Fibre Channel
Forwarders on your network, and tells you which VLANs they are operating on.

fipvlan must be run as root, or another used with the network administrator
capability granted.

Changes since 1st posting:

fipvlan can now run over multiple Ethernet interfaces in one run, you simply
specify more than one interface on the command line.  Network interface names
are no longer proceeded with -i or --interface, simply list them.

There is now an --auto (or -a) option for enabling automatic interface
selection.  If you turn on automatic mode, fipvlan will perform FIP VLAN
discovery on all active (IFF_RUNNING) Ethernet interfaces in the system.

Signed-off-by: Chris Leech <christopher.leech at intel.com>
---

 .gitignore     |    1 
 Makefile.am    |    6 
 fipvlan.c      |  712 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 include/fip.h  |  126 ++++++++++
 include/list.h |  444 +++++++++++++++++++++++++++++++++++
 include/log.h  |   15 +
 log.c          |   87 +++++++
 7 files changed, 1390 insertions(+), 1 deletions(-)
 create mode 100644 fipvlan.c
 create mode 100644 include/fip.h
 create mode 100644 include/list.h
 create mode 100644 include/log.h
 create mode 100644 log.c

diff --git a/.gitignore b/.gitignore
index 974800d..a15894c 100644
--- a/.gitignore
+++ b/.gitignore
@@ -20,6 +20,7 @@ fcoeplumb
 *.o
 fcoeadm
 fcoemon
+fipvlan
 etc/initd/fcoe
 
 # build.sh generated files
diff --git a/Makefile.am b/Makefile.am
index 5d896bf..f32915d 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1,5 +1,5 @@
 ## target programs, to be built and installed in $(prefix)/sbin
-sbin_PROGRAMS = fcoeadm
+sbin_PROGRAMS = fcoeadm fipvlan
 if WITH_DCB
 sbin_PROGRAMS += fcoemon
 endif
@@ -28,6 +28,10 @@ include/fc_scsi.h include/fc_types.h include/net_types.h
 fcoemon_CFLAGS = $(DCBD_CFLAGS)
 fcoemon_LDFLAGS = -lrt
 
+## rules for building fipvlan
+## only listed sources get packaged, so must list all headers too
+fipvlan_SOURCES = fipvlan.c include/fip.h log.c include/log.h include/list.h
+
 ## install configuration file in $(prefix)/etc/fcoe
 fcoe_configdir = ${sysconfdir}/fcoe
 dist_fcoe_config_DATA = etc/config etc/cfg-ethx
diff --git a/fipvlan.c b/fipvlan.c
new file mode 100644
index 0000000..6d98139
--- /dev/null
+++ b/fipvlan.c
@@ -0,0 +1,712 @@
+/*
+ * Copyright(c) 2009 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Maintained at www.Open-FCoE.org
+ */
+
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <stdarg.h>
+#include <stdint.h>
+#include <unistd.h>
+#include <errno.h>
+#include <getopt.h>
+#include <poll.h>
+#include <signal.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <net/if_arp.h>
+#include <net/ethernet.h>
+#include <netpacket/packet.h>
+#include <arpa/inet.h>
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+#include "fip.h"
+#include "log.h"
+#include "list.h"
+
+#define ARRAY_SIZE(a)	(sizeof(a) / sizeof((a)[0]))
+
+/* global configuration */
+
+char *exe;
+#define VERSION_STR	"0.5"
+
+struct iff {
+	int ifindex;
+	char *ifname;
+	unsigned char mac_addr[ETHER_ADDR_LEN];
+	struct list_head list;
+};
+
+LIST_HEAD(interfaces);
+
+struct fcf {
+	struct iff *interface;
+	uint16_t vlan;
+	unsigned char mac_addr[ETHER_ADDR_LEN];
+	struct list_head list;
+};
+
+LIST_HEAD(fcfs);
+
+/**
+ * packet_socket - create a packet socket bound to the FIP ethertype
+ */
+int packet_socket(void)
+{
+	int s;
+
+	log_debug(1, "creating ETH_P_FIP packet socket");
+	s = socket(PF_PACKET, SOCK_DGRAM, htons(ETH_P_FIP));
+	if (s < 0)
+		log_errno("packet socket error");
+
+	return s;
+}
+
+/**
+ * fip_send_vlan_request - send a FIP VLAN request
+ * @s: ETH_P_FIP packet socket
+ * @iff: network interface to send from
+ *
+ * Note: sends to FIP_ALL_FCF_MACS
+ */
+ssize_t fip_send_vlan_request(int s, struct iff *iff)
+{
+	struct sockaddr_ll sa = {
+		.sll_family = AF_PACKET,
+		.sll_protocol = htons(ETH_P_FIP),
+		.sll_ifindex = iff->ifindex,
+		.sll_hatype = ARPHRD_ETHER,
+		.sll_pkttype = PACKET_MULTICAST,
+		.sll_halen = ETHER_ADDR_LEN,
+		.sll_addr = FIP_ALL_FCF_MACS,
+	};
+	struct fiphdr fh = {
+		.fip_version = FIP_VERSION(1),
+		.fip_proto = htons(FIP_PROTO_VLAN),
+		.fip_subcode = FIP_VLAN_REQ,
+		.fip_desc_len = htons(2),
+		.fip_flags = 0,
+	};
+	struct {
+		struct fip_tlv_mac_addr mac;
+	} tlvs = {
+		.mac = {
+			.hdr.tlv_type = FIP_TLV_MAC_ADDR,
+			.hdr.tlv_len = 2,
+		},
+	};
+	struct iovec iov[] = {
+		{ .iov_base = &fh, .iov_len = sizeof(fh), },
+		{ .iov_base = &tlvs, .iov_len = sizeof(tlvs), },
+	};
+	struct msghdr msg = {
+		.msg_name = &sa,
+		.msg_namelen = sizeof(sa),
+		.msg_iov = iov,
+		.msg_iovlen = ARRAY_SIZE(iov),
+	};
+	int rc;
+
+	memcpy(tlvs.mac.mac_addr, iff->mac_addr, ETHER_ADDR_LEN);
+
+	log_debug(1, "sending FIP VLAN request");
+	rc = sendmsg(s, &msg, 0);
+	if (rc < 0)
+		log_errno("sendmsg error");
+
+	return rc;
+}
+
+struct fip_tlv_ptrs {
+	struct fip_tlv_mac_addr		*mac;
+	struct fip_tlv_vlan		*vlan[370];
+	unsigned int 			vlanc;
+};
+
+#define SET_BIT(b, n)	((b) |= (1 << (n)))
+
+#define TLV_LEN_CHECK(t, l) ({ \
+	int _tlc = ((t)->tlv_len != (l)) ? 1 : 0; \
+	if (_tlc) \
+		log_warn("bad length for TLV of type %d", (t)->tlv_type); \
+	_tlc; \
+})
+
+/**
+ * fip_parse_tlvs - parse type/length/value encoded FIP descriptors
+ * @ptr: pointer to beginning of FIP TLV payload, the first descriptor
+ * @len: total length of all TLVs, in double words
+ * @tlv_ptrs: pointers to type specific structures to fill out
+ */
+unsigned int fip_parse_tlvs(void *ptr, int len, struct fip_tlv_ptrs *tlv_ptrs)
+{
+	struct fip_tlv_hdr *tlv = ptr;
+	unsigned int bitmap = 0;
+
+	tlv_ptrs->vlanc = 0;
+	while (len > 0) {
+		switch (tlv->tlv_type) {
+		case FIP_TLV_MAC_ADDR:
+			if (TLV_LEN_CHECK(tlv, 2))
+				break;
+			SET_BIT(bitmap, FIP_TLV_MAC_ADDR);
+			tlv_ptrs->mac = (struct fip_tlv_mac_addr *) tlv;
+			break;
+		case FIP_TLV_VLAN:
+			if (TLV_LEN_CHECK(tlv, 1))
+				break;
+			SET_BIT(bitmap, FIP_TLV_VLAN);
+			tlv_ptrs->vlan[tlv_ptrs->vlanc++] = (void *) tlv;
+			break;
+		default:
+			/* unexpected or unrecognized descriptor */
+			log_warn("unrecognized TLV type %d", tlv->tlv_type);
+			break;
+		}
+		len -= tlv->tlv_len;
+		tlv = ((void *) tlv) + (tlv->tlv_len << 2);
+	};
+	return bitmap;
+}
+
+/**
+ * fip_recv_vlan_note - parse a FIP VLAN Notification
+ * @fh: FIP header, the beginning of the received FIP frame
+ * @len: total length of the received FIP frame
+ * @iff: interface this notification was received on
+ */
+int fip_recv_vlan_note(struct fiphdr *fh, ssize_t len, struct iff *iff)
+{
+	struct fip_tlv_ptrs tlvs;
+	struct fcf *fcf;
+	unsigned int bitmap, required_tlvs;
+	int desc_len;
+	int i;
+
+	log_debug(1, "received FIP VLAN Notification");
+
+	desc_len = ntohs(fh->fip_desc_len);
+	if (len < (sizeof(*fh) + (desc_len << 2)))
+		return -1;
+
+	required_tlvs = (1 << FIP_TLV_MAC_ADDR) | (1 << FIP_TLV_VLAN);
+
+	bitmap = fip_parse_tlvs((fh + 1), desc_len, &tlvs);
+	if ((bitmap & required_tlvs) != required_tlvs)
+		return -1;
+
+	for (i = 0; i < tlvs.vlanc; i++) {
+		fcf = malloc(sizeof(*fcf));
+		if (!fcf) {
+			log_errno("malloc failed");
+			break;
+		}
+		memset(fcf, 0, sizeof(*fcf));
+		fcf->interface = iff;
+		fcf->vlan = ntohs(tlvs.vlan[i]->vlan);
+		memcpy(fcf->mac_addr, tlvs.mac->mac_addr, ETHER_ADDR_LEN);
+		list_add_tail(&fcf->list, &fcfs);
+	}
+
+	return 0;
+}
+
+/**
+ * fip_recv - receive from a FIP packet socket
+ * @s: packet socket with data ready to be received
+ */
+int fip_recv(int s)
+{
+	char buf[4096];
+	struct sockaddr_ll sa;
+	struct iovec iov[] = {
+		{ .iov_base = &buf[0], .iov_len = sizeof(buf), },
+	};
+	struct msghdr msg = {
+		.msg_name = &sa,
+		.msg_namelen = sizeof(sa),
+		.msg_iov = iov,
+		.msg_iovlen = ARRAY_SIZE(iov),
+	};
+	struct fiphdr *fh;
+	struct iff *iff;
+	ssize_t len;
+
+	log_debug(1, "%s", __func__);
+
+	len = recvmsg(s, &msg, 0);
+	if (len < 0) {
+		log_errno("packet socket recv error");
+		return len;
+	}
+
+	list_for_each_entry(iff, &interfaces, list) {
+		if (iff->ifindex == sa.sll_ifindex)
+			break;
+	}
+	if (&iff->list == &interfaces) {
+		log_err("received packet on unexpected interface");
+		return -1;
+	}
+
+	if (len < sizeof(*fh)) {
+		log_err("received packed smaller that FIP header length");
+		return -1;
+	}
+
+	fh = (struct fiphdr *) buf;
+
+	/* We only care about VLAN Notifications */
+	if (ntohs(fh->fip_proto) != FIP_PROTO_VLAN) {
+		log_debug(1, "ignoring FIP packet, protocol %d",
+			  ntohs(fh->fip_proto));
+		return -1;
+	}
+
+	switch (fh->fip_subcode) {
+	case FIP_VLAN_NOTE:
+		fip_recv_vlan_note(fh, len, iff);
+		break;
+	default:
+		log_warn("FIP packet with unknown subcode %d", fh->fip_subcode);
+		return -1;
+	}
+
+	return 0;
+}
+
+/**
+ * rtnl_socket - create and bind a routing netlink socket
+ */
+int rtnl_socket(void)
+{
+	struct sockaddr_nl sa = {
+		.nl_family = AF_NETLINK,
+		.nl_pid = getpid(),
+		.nl_groups = RTMGRP_LINK,
+	};
+	int s;
+	int rc;
+
+	log_debug(1, "creating netlink socket");
+	s = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
+	if (s < 0) {
+		log_errno("netlink socket error");
+		return s;
+	}
+
+	rc = bind(s, (struct sockaddr *) &sa, sizeof(sa));
+	if (rc < 0) {
+		log_errno("netlink bind error");
+		close(s);
+		return rc;
+	}
+
+	return s;
+}
+
+/**
+ * send_getlink_dump - send an RTM_GETLINK dump request to list all interfaces
+ * @s: routing netlink socket to use
+ */
+ssize_t send_getlink_dump(int s)
+{
+	struct sockaddr_nl sa = {
+		.nl_family = AF_NETLINK,
+		.nl_pid = 0,
+	};
+	struct {
+		struct nlmsghdr nh;
+		struct ifinfomsg ifm;
+	} req = {
+		.nh = {
+			.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)),
+			.nlmsg_type = RTM_GETLINK,
+			.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP,
+			.nlmsg_pid = 0,
+		},
+		.ifm = {
+			.ifi_family = AF_UNSPEC,
+			.ifi_type = ARPHRD_ETHER,
+		},
+	};
+	struct iovec iov[] = {
+		{ .iov_base = &req, .iov_len = sizeof(req), },
+	};
+	struct msghdr msg = {
+		.msg_name = &sa,
+		.msg_namelen = sizeof(sa),
+		.msg_iov = iov,
+		.msg_iovlen = ARRAY_SIZE(iov),
+	};
+	int rc;
+
+	log_debug(1, "sending RTM_GETLINK dump request");
+	rc = sendmsg(s, &msg, 0);
+	if (rc < 0)
+		log_errno("netlink sendmsg error");
+
+	return rc;
+}
+
+/**
+ * rtnl_recv_newlink - parse response to RTM_GETLINK, or an RTM_NEWLINK event
+ * @nh: netlink message header, beginning of received netlink frame
+ */
+void rtnl_recv_newlink(struct nlmsghdr *nh)
+{
+	struct ifinfomsg *ifm;
+	struct rtattr *rta;
+	struct iff *iff;
+	unsigned int len;
+
+	log_debug(1, "RTM_NEWLINK");
+
+	ifm = NLMSG_DATA(nh);
+	log_debug(1, "ifindex %d, type %d", ifm->ifi_index, ifm->ifi_type);
+
+	/* We only deal with Ethernet interfaces */
+	if (ifm->ifi_type != ARPHRD_ETHER)
+		return;
+
+	/* if there's no link, we're not going to wait for it */
+	if ((ifm->ifi_flags & IFF_RUNNING) != IFF_RUNNING)
+		return;
+
+	iff = malloc(sizeof(*iff));
+	if (!iff) {
+		log_errno("malloc failed");
+		return;
+	}
+	memset(iff, 0, sizeof(*iff));
+
+	iff->ifindex = ifm->ifi_index;
+
+	len = IFLA_PAYLOAD(nh);
+	for (rta = IFLA_RTA(ifm); RTA_OK(rta, len); rta = RTA_NEXT(rta, len)) {
+		switch (rta->rta_type) {
+		case IFLA_ADDRESS:
+			memcpy(iff->mac_addr, RTA_DATA(rta), ETHER_ADDR_LEN);
+			log_debug(1, "\tIFLA_ADDRESS\t%x:%x:%x:%x:%x:%x",
+					iff->mac_addr[0], iff->mac_addr[1],
+					iff->mac_addr[2], iff->mac_addr[3],
+					iff->mac_addr[4], iff->mac_addr[5]);
+			break;
+		case IFLA_IFNAME:
+			iff->ifname = strdup(RTA_DATA(rta));
+			log_debug(1, "\tIFLA_IFNAME\t%s", iff->ifname);
+			break;
+		default:
+			/* other attributes don't matter */
+			break;
+		}
+	}
+
+	list_add_tail(&iff->list, &interfaces);
+}
+
+#define NLMSG(c) ((struct nlmsghdr *) (c))
+
+/**
+ * rtnl_recv - receive from a routing netlink socket
+ * @s: routing netlink socket with data ready to be received
+ *
+ * Returns:	0 when NLMSG_DONE is received
+ * 		<0 on error
+ * 		>0 when more data is expected
+ */
+int rtnl_recv(int s)
+{
+	char buf[8192];
+	struct sockaddr_nl sa;
+	struct iovec iov[] = {
+		[0] = { .iov_base = buf, .iov_len = sizeof(buf), },
+	};
+	struct msghdr msg = {
+		.msg_name = &sa,
+		.msg_namelen = sizeof(sa),
+		.msg_iov = iov,
+		.msg_iovlen = ARRAY_SIZE(iov),
+	};
+	struct nlmsghdr *nh;
+	int len;
+	int rc;
+
+	log_debug(1, "%s", __func__);
+
+	len = recvmsg(s, &msg, 0);
+	if (len < 0) {
+		log_errno("netlink recvmsg error");
+		return len;
+	}
+
+	rc = 1;
+	for (nh = NLMSG(buf); NLMSG_OK(nh, len); nh = NLMSG_NEXT(nh, len)) {
+		switch (nh->nlmsg_type) {
+		case RTM_NEWLINK:
+			rtnl_recv_newlink(nh);
+			break;
+		case NLMSG_DONE:
+			log_debug(1, "NLMSG_DONE");
+			break;
+		case NLMSG_ERROR:
+			log_debug(1, "NLMSG_ERROR");
+			break;
+		default:
+			log_warn("unexpected netlink message type %d",
+				 nh->nlmsg_type);
+			break;
+		}
+
+		if (nh->nlmsg_type == NLMSG_DONE) {
+			rc = 0;
+			break;
+		}
+		if (!(nh->nlmsg_flags & NLM_F_MULTI))
+			break;
+	}
+	return rc;
+}
+
+/* command line arguments */
+
+#define GETOPT_STR "ahv"
+
+static const struct option long_options[] = {
+	{ "auto", no_argument, NULL, 'a' },
+	{ "help", no_argument, NULL, 'h' },
+	{ "version", no_argument, NULL, 'v' },
+	{ NULL, 0, NULL, 0 }
+};
+
+static void help(int status)
+{
+	printf(
+"Usage: %s [ options ] [ network interfaces ]\n"
+"Options:\n"
+"  -a, --auto           Auto select Ethernet interfaces\n"
+"  -h, --help           Display this help and exit\n"
+"  -v, --version        Display version information and exit\n",
+	exe);
+
+	exit(status);
+}
+
+/* array of interface names to use */
+char **namev;
+/* length of namev */
+int namec;
+
+int parse_cmdline(int argc, char **argv)
+{
+	char c;
+	int automode = 0;
+
+	while (1) {
+		c = getopt_long(argc, argv, GETOPT_STR, long_options, NULL);
+		if (c < 0)
+			break;
+		switch (c) {
+		case 'a':
+			automode = 1;
+			break;
+		case 'h':
+			help(0);
+			break;
+		case 'v':
+			printf("%s version %s\n", exe, VERSION_STR);
+			exit(0);
+			break;
+		default:
+			fprintf(stderr, "Try '%s --help' "
+					"for more information\n", exe);
+			exit(1);
+		}
+	}
+
+	if ((optind == argc) && (!automode))
+		help(1);
+
+	namev = &argv[optind];
+	namec = argc - optind;
+	return automode;
+}
+
+/* exit after waiting 2 seconds without receiving anything */
+#define TIMEOUT 2000
+
+int autodetect()
+{
+	struct pollfd pfd[1];
+	int ns;
+	int rc;
+
+	ns = rtnl_socket();
+	if (ns < 0)
+		return ns;
+
+	send_getlink_dump(ns);
+	pfd[0].fd = ns;
+	pfd[0].events = POLLIN;
+
+	while (1) {
+		rc = poll(pfd, ARRAY_SIZE(pfd), TIMEOUT);
+		log_debug(1, "return from poll %d", rc);
+		if (rc == 0) /* timeout */
+			break;
+		if (rc == -1) {
+			log_errno("poll error");
+			break;
+		}
+		if (pfd[0].revents) {
+			rc = rtnl_recv(pfd[0].fd);
+			if (rc == 0)
+				break;
+		}
+		pfd[0].revents = 0;
+	}
+	close(ns);
+	return 0;
+}
+
+int check_interface(char *name, int ps)
+{
+	struct ifreq ifr;
+	struct iff *iff;
+
+	iff = malloc(sizeof(*iff));
+	if (!iff) {
+		log_errno("malloc failed");
+		return -1;
+	}
+	memset(iff, 0, sizeof(*iff));
+
+	strncpy(ifr.ifr_name, name, IFNAMSIZ);
+	if (ioctl(ps, SIOCGIFINDEX, &ifr) != 0) {
+		log_errno("SIOCGIFINDEX");
+		goto err;
+	}
+	iff->ifname = strdup(ifr.ifr_name);
+	iff->ifindex = ifr.ifr_ifindex;
+
+	if (ioctl(ps, SIOCGIFHWADDR, &ifr) != 0) {
+		log_errno("SIOCGIFHWADDR");
+		goto err;
+	}
+	if (ifr.ifr_addr.sa_family != ARPHRD_ETHER) {
+		log_err("%s is not an Ethernet interface", name);
+		goto err;
+	}
+	memcpy(iff->mac_addr, ifr.ifr_addr.sa_data, ETHER_ADDR_LEN);
+
+	list_add_tail(&iff->list, &interfaces);
+	return 0;
+err:
+	free(iff);
+	return -1;
+}
+
+void print_results()
+{
+	struct fcf *fcf;
+
+	if (list_empty(&fcfs)) {
+		printf("No Fibre Channel Forwarders Found\n");
+		return;
+	}
+
+	printf("Fibre Channel Forwarders Discovered\n");
+	printf("%-10.10s| %-5.5s| %-10.10s\n", "interface", "VLAN", "FCF MAC");
+	printf("------------------------------------\n");
+	list_for_each_entry(fcf, &fcfs, list) {
+		printf("%-10.10s| %-5d| %2.2x:%2.2x:%2.2x:%2.2x:%2.2x:%2.2x\n",
+			fcf->interface->ifname, fcf->vlan,
+			fcf->mac_addr[0], fcf->mac_addr[1], fcf->mac_addr[2],
+			fcf->mac_addr[3], fcf->mac_addr[4], fcf->mac_addr[5]);
+	}
+}
+
+void recv_loop(int ps)
+{
+	struct pollfd pfd[1] = {
+		[0].fd = ps,
+		[0].events = POLLIN,
+	};
+	int rc;
+
+	while (1) {
+		rc = poll(pfd, ARRAY_SIZE(pfd), TIMEOUT);
+		log_debug(1, "return from poll %d", rc);
+		if (rc == 0) /* timeout */
+			break;
+		if (rc == -1) {
+			log_errno("poll error");
+			break;
+		}
+		if (pfd[0].revents)
+			fip_recv(pfd[0].fd);
+		pfd[0].revents = 0;
+	}
+}
+
+int main(int argc, char **argv)
+{
+	int ps;
+	struct iff *iff;
+	int i;
+	int automode;
+
+	exe = strrchr(argv[0], '/');
+	if (exe)
+		exe++;
+	else
+		exe = argv[0];
+
+	automode = parse_cmdline(argc, argv);
+	log_start(exe, 0, 0);
+
+	ps = packet_socket();
+
+	if (automode) {
+		autodetect();
+	} else {
+		for (i = 0; i < namec; i++)
+			check_interface(namev[i], ps);
+	}
+
+	if (list_empty(&interfaces)) {
+		log_err("no interfaces to perform discovery on");
+		close(ps);
+		log_stop();
+		exit(1);
+	}
+
+	list_for_each_entry(iff, &interfaces, list)
+		fip_send_vlan_request(ps, iff);
+
+	recv_loop(ps);
+	print_results();
+
+	close(ps);
+	log_stop();
+	exit(0);
+}
+
diff --git a/include/fip.h b/include/fip.h
new file mode 100644
index 0000000..91a017f
--- /dev/null
+++ b/include/fip.h
@@ -0,0 +1,126 @@
+/*
+ * Copyright(c) 2009 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Maintained at www.Open-FCoE.org
+ */
+
+#ifndef FIP_H
+#define FIP_H
+
+#define ETH_P_FCOE	0x8906
+#define ETH_P_FIP	0x8914
+
+#define FIP_ALL_FCOE_MACS	{ 0x01, 0x10, 0x18, 0x01, 0x00, 0x00 }
+#define FIP_ALL_FCF_MACS	{ 0x01, 0x10, 0x18, 0x01, 0x00, 0x01 }
+#define FIP_ALL_ENODE_MACS	{ 0x01, 0x10, 0x18, 0x01, 0x00, 0x02 }
+
+struct fiphdr {
+	uint8_t		fip_version;	/* version, upper 4 bits only */
+	uint8_t		__resvd_0;
+	uint16_t	fip_proto;	/* protocol code */
+	uint8_t		__resvd_1;
+	uint8_t		fip_subcode;	/* subcode */
+	uint16_t	fip_desc_len;	/* descriptor list length */
+	uint16_t	fip_flags;
+};
+
+#define FIP_VERSION(n)	(n << 4)
+#define FIP_F_FP	(1 << 15)	/* FPMA supported/requested/granted */
+#define FIP_F_SP	(1 << 14)	/* SPMA supported/requested/granted */
+#define FIP_F_A		(1 << 2)	/* Available for Login */
+#define FIP_F_S		(1 << 1)	/* Solicited advertisement */
+#define FIP_F_F		(1 << 0)	/* FCF */
+
+/* FCF Discovery Protocol */
+#define FIP_PROTO_DISC	1
+#define FIP_DISC_SOL	1
+#define FIP_DISC_ADV	2
+
+/* Virtual Link Instantiation (encapsulated ELS) */
+#define FIP_PROTO_VLI	2
+#define FIP_VLI_REQ	1
+#define FIP_VLI_REPLY	2
+
+/* FIP Keep Alive */
+#define FIP_PROTO_FKA	3
+#define FIP_FKA		1
+#define FIP_FKA_CLEAR	2
+
+/* VLAN Discovery */
+#define FIP_PROTO_VLAN	4
+#define FIP_VLAN_REQ	1
+#define FIP_VLAN_NOTE	2
+
+struct fip_tlv_hdr {
+	uint8_t		tlv_type;
+	uint8_t		tlv_len;	/* length in quad-words of entire TLV */
+};
+
+#define FIP_TLV_PRIORITY		1
+#define FIP_TLV_MAC_ADDR		2
+#define FIP_TLV_FC_MAP			3
+#define FIP_TLV_NAME_IDENTIFIER		4
+#define FIP_TLV_FABRIC_NAME		5
+#define FIP_TLV_MAX_RECV_SIZE		6
+#define FIP_TLV_FLOGI			7
+#define FIP_TLV_FDISC			8
+#define FIP_TLV_LOGO			9
+#define FIP_TLV_ELP			10
+
+#define FIP_TLV_VLAN			14
+
+#define DEFAULT_FIP_PRIORITY		128
+
+/* Priority Descriptor */
+struct fip_tlv_priority {
+	struct fip_tlv_hdr hdr;
+	unsigned char __resvd;
+	uint8_t priority;
+};
+
+/* MAC Address Descriptor */
+struct fip_tlv_mac_addr {
+	struct fip_tlv_hdr hdr;
+	unsigned char mac_addr[ETHER_ADDR_LEN];
+};
+
+/* FC-MAP Descriptor */
+struct fip_tlv_fc_map {
+	struct fip_tlv_hdr hdr;
+	unsigned char __resvd[3];
+	uint8_t map[3];
+};
+
+/* Name Identifier Descriptor (also used for Fabric Name Descriptor) */
+struct fip_tlv_name_id {
+	struct fip_tlv_hdr hdr;
+	unsigned char __resvd[2];
+	unsigned char wwn[8];
+};
+
+/* Max Receive Size Descriptor */
+struct fip_tlv_max_recv_size {
+	struct fip_tlv_hdr hdr;
+	uint16_t mtu;
+};
+
+/* VLAN */
+struct fip_tlv_vlan {
+	struct fip_tlv_hdr hdr;
+	uint16_t vlan;	/* only lower 12 bits matter */
+};
+
+#endif /* FIP_H */
diff --git a/include/list.h b/include/list.h
new file mode 100644
index 0000000..35da9fd
--- /dev/null
+++ b/include/list.h
@@ -0,0 +1,444 @@
+/* Adapted from the Linux kernel, under the terms of the GPLv2 */
+
+#ifndef LIST_H
+#define LIST_H
+
+#include <stddef.h>
+
+#define container_of(ptr, type, member) ({ \
+	const typeof(((type *) 0)->member) * __mptr = (ptr); \
+	(type *)((char *) __mptr - offsetof(type, member)); \
+})
+
+/*
+ * Simple doubly linked list implementation.
+ *
+ * Some of the internal functions ("__xxx") are useful when
+ * manipulating whole lists rather than single entries, as
+ * sometimes we already know the next/prev entries and we can
+ * generate better code by using them directly rather than
+ * using the generic single-entry routines.
+ */
+
+struct list_head {
+	struct list_head *next, *prev;
+};
+
+#define LIST_HEAD_INIT(name) { &(name), &(name) }
+
+#define LIST_HEAD(name) \
+	struct list_head name = LIST_HEAD_INIT(name)
+
+static inline void INIT_LIST_HEAD(struct list_head *list)
+{
+	list->next = list;
+	list->prev = list;
+}
+
+/*
+ * Insert a new entry between two known consecutive entries.
+ *
+ * This is only for internal list manipulation where we know
+ * the prev/next entries already!
+ */
+static inline void __list_add(struct list_head *new,
+			      struct list_head *prev,
+			      struct list_head *next)
+{
+	next->prev = new;
+	new->next = next;
+	new->prev = prev;
+	prev->next = new;
+}
+
+/**
+ * list_add - add a new entry
+ * @new: new entry to be added
+ * @head: list head to add it after
+ *
+ * Insert a new entry after the specified head.
+ * This is good for implementing stacks.
+ */
+static inline void list_add(struct list_head *new, struct list_head *head)
+{
+	__list_add(new, head, head->next);
+}
+
+/**
+ * list_add_tail - add a new entry
+ * @new: new entry to be added
+ * @head: list head to add it before
+ *
+ * Insert a new entry before the specified head.
+ * This is useful for implementing queues.
+ */
+static inline void list_add_tail(struct list_head *new, struct list_head *head)
+{
+	__list_add(new, head->prev, head);
+}
+
+/*
+ * Delete a list entry by making the prev/next entries
+ * point to each other.
+ *
+ * This is only for internal list manipulation where we know
+ * the prev/next entries already!
+ */
+static inline void __list_del(struct list_head *prev, struct list_head *next)
+{
+	next->prev = prev;
+	prev->next = next;
+}
+
+/**
+ * list_del - deletes entry from list.
+ * @entry: the element to delete from the list.
+ * Note: list_empty() on entry does not return true after this, the entry is
+ * in an undefined state.
+ */
+static inline void list_del(struct list_head *entry)
+{
+	__list_del(entry->prev, entry->next);
+	entry->next = NULL;
+	entry->prev = NULL;
+}
+
+/**
+ * list_replace - replace old entry by new one
+ * @old : the element to be replaced
+ * @new : the new element to insert
+ *
+ * If @old was empty, it will be overwritten.
+ */
+static inline void list_replace(struct list_head *old,
+				struct list_head *new)
+{
+	new->next = old->next;
+	new->next->prev = new;
+	new->prev = old->prev;
+	new->prev->next = new;
+}
+
+static inline void list_replace_init(struct list_head *old,
+					struct list_head *new)
+{
+	list_replace(old, new);
+	INIT_LIST_HEAD(old);
+}
+
+/**
+ * list_del_init - deletes entry from list and reinitialize it.
+ * @entry: the element to delete from the list.
+ */
+static inline void list_del_init(struct list_head *entry)
+{
+	__list_del(entry->prev, entry->next);
+	INIT_LIST_HEAD(entry);
+}
+
+/**
+ * list_move - delete from one list and add as another's head
+ * @list: the entry to move
+ * @head: the head that will precede our entry
+ */
+static inline void list_move(struct list_head *list, struct list_head *head)
+{
+	__list_del(list->prev, list->next);
+	list_add(list, head);
+}
+
+/**
+ * list_move_tail - delete from one list and add as another's tail
+ * @list: the entry to move
+ * @head: the head that will follow our entry
+ */
+static inline void list_move_tail(struct list_head *list,
+				  struct list_head *head)
+{
+	__list_del(list->prev, list->next);
+	list_add_tail(list, head);
+}
+
+/**
+ * list_is_last - tests whether @list is the last entry in list @head
+ * @list: the entry to test
+ * @head: the head of the list
+ */
+static inline int list_is_last(const struct list_head *list,
+				const struct list_head *head)
+{
+	return list->next == head;
+}
+
+/**
+ * list_empty - tests whether a list is empty
+ * @head: the list to test.
+ */
+static inline int list_empty(const struct list_head *head)
+{
+	return head->next == head;
+}
+
+/**
+ * list_empty_careful - tests whether a list is empty and not being modified
+ * @head: the list to test
+ *
+ * Description:
+ * tests whether a list is empty _and_ checks that no other CPU might be
+ * in the process of modifying either member (next or prev)
+ *
+ * NOTE: using list_empty_careful() without synchronization
+ * can only be safe if the only activity that can happen
+ * to the list entry is list_del_init(). Eg. it cannot be used
+ * if another CPU could re-list_add() it.
+ */
+static inline int list_empty_careful(const struct list_head *head)
+{
+	struct list_head *next = head->next;
+	return (next == head) && (next == head->prev);
+}
+
+/**
+ * list_is_singular - tests whether a list has just one entry.
+ * @head: the list to test.
+ */
+static inline int list_is_singular(const struct list_head *head)
+{
+	return !list_empty(head) && (head->next == head->prev);
+}
+
+static inline void __list_splice(const struct list_head *list,
+				 struct list_head *head)
+{
+	struct list_head *first = list->next;
+	struct list_head *last = list->prev;
+	struct list_head *at = head->next;
+
+	first->prev = head;
+	head->next = first;
+
+	last->next = at;
+	at->prev = last;
+}
+
+/**
+ * list_splice - join two lists
+ * @list: the new list to add.
+ * @head: the place to add it in the first list.
+ */
+static inline void list_splice(const struct list_head *list,
+				struct list_head *head)
+{
+	if (!list_empty(list))
+		__list_splice(list, head);
+}
+
+/**
+ * list_splice_init - join two lists and reinitialise the emptied list.
+ * @list: the new list to add.
+ * @head: the place to add it in the first list.
+ *
+ * The list at @list is reinitialised
+ */
+static inline void list_splice_init(struct list_head *list,
+				    struct list_head *head)
+{
+	if (!list_empty(list)) {
+		__list_splice(list, head);
+		INIT_LIST_HEAD(list);
+	}
+}
+
+/**
+ * list_entry - get the struct for this entry
+ * @ptr:	the &struct list_head pointer.
+ * @type:	the type of the struct this is embedded in.
+ * @member:	the name of the list_struct within the struct.
+ */
+#define list_entry(ptr, type, member) \
+	container_of(ptr, type, member)
+
+/**
+ * list_first_entry - get the first element from a list
+ * @ptr:	the list head to take the element from.
+ * @type:	the type of the struct this is embedded in.
+ * @member:	the name of the list_struct within the struct.
+ *
+ * Note, that list is expected to be not empty.
+ */
+#define list_first_entry(ptr, type, member) \
+	list_entry((ptr)->next, type, member)
+
+/**
+ * list_for_each	-	iterate over a list
+ * @pos:	the &struct list_head to use as a loop cursor.
+ * @head:	the head for your list.
+ */
+#define list_for_each(pos, head) \
+	for (pos = (head)->next; pos != (head); pos = pos->next)
+
+/**
+ * list_for_each_prev	-	iterate over a list backwards
+ * @pos:	the &struct list_head to use as a loop cursor.
+ * @head:	the head for your list.
+ */
+#define list_for_each_prev(pos, head) \
+	for (pos = (head)->prev; pos != (head); pos = pos->prev)
+
+/**
+ * list_for_each_safe - iterate over a list safe against removal of list entry
+ * @pos:	the &struct list_head to use as a loop cursor.
+ * @n:		another &struct list_head to use as temporary storage
+ * @head:	the head for your list.
+ */
+#define list_for_each_safe(pos, n, head) \
+	for (pos = (head)->next, n = pos->next; \
+	     pos != (head); \
+	     pos = n, n = pos->next)
+
+/**
+ * list_for_each_prev_safe - iterate over a list backwards safe against removal of list entry
+ * @pos:	the &struct list_head to use as a loop cursor.
+ * @n:		another &struct list_head to use as temporary storage
+ * @head:	the head for your list.
+ */
+#define list_for_each_prev_safe(pos, n, head) \
+	for (pos = (head)->prev, n = pos->prev; \
+	     pos != (head); \
+	     pos = n, n = pos->prev)
+
+/**
+ * list_for_each_entry	-	iterate over list of given type
+ * @pos:	the type * to use as a loop cursor.
+ * @head:	the head for your list.
+ * @member:	the name of the list_struct within the struct.
+ */
+#define list_for_each_entry(pos, head, member)				\
+	for (pos = list_entry((head)->next, typeof(*pos), member);	\
+	     &pos->member != (head); 	\
+	     pos = list_entry(pos->member.next, typeof(*pos), member))
+
+/**
+ * list_for_each_entry_reverse - iterate backwards over list of given type.
+ * @pos:	the type * to use as a loop cursor.
+ * @head:	the head for your list.
+ * @member:	the name of the list_struct within the struct.
+ */
+#define list_for_each_entry_reverse(pos, head, member)			\
+	for (pos = list_entry((head)->prev, typeof(*pos), member);	\
+	     &pos->member != (head); 	\
+	     pos = list_entry(pos->member.prev, typeof(*pos), member))
+
+/**
+ * list_prepare_entry - prepare a pos entry for use in list_for_each_entry_continue()
+ * @pos:	the type * to use as a start point
+ * @head:	the head of the list
+ * @member:	the name of the list_struct within the struct.
+ *
+ * Prepares a pos entry for use as a start point in
+ * list_for_each_entry_continue().
+ */
+#define list_prepare_entry(pos, head, member) \
+	((pos) ? : list_entry(head, typeof(*pos), member))
+
+/**
+ * list_for_each_entry_continue - continue iteration over list of given type
+ * @pos:	the type * to use as a loop cursor.
+ * @head:	the head for your list.
+ * @member:	the name of the list_struct within the struct.
+ *
+ * Continue to iterate over list of given type, continuing after
+ * the current position.
+ */
+#define list_for_each_entry_continue(pos, head, member) 		\
+	for (pos = list_entry(pos->member.next, typeof(*pos), member);	\
+	     &pos->member != (head);	\
+	     pos = list_entry(pos->member.next, typeof(*pos), member))
+
+/**
+ * list_for_each_entry_continue_reverse - iterate backwards from the given point
+ * @pos:	the type * to use as a loop cursor.
+ * @head:	the head for your list.
+ * @member:	the name of the list_struct within the struct.
+ *
+ * Start to iterate over list of given type backwards, continuing after
+ * the current position.
+ */
+#define list_for_each_entry_continue_reverse(pos, head, member)		\
+	for (pos = list_entry(pos->member.prev, typeof(*pos), member);	\
+	     &pos->member != (head);	\
+	     pos = list_entry(pos->member.prev, typeof(*pos), member))
+
+/**
+ * list_for_each_entry_from - iterate over list of given type from the current point
+ * @pos:	the type * to use as a loop cursor.
+ * @head:	the head for your list.
+ * @member:	the name of the list_struct within the struct.
+ *
+ * Iterate over list of given type, continuing from current position.
+ */
+#define list_for_each_entry_from(pos, head, member) 			\
+	for (; &pos->member != (head);	\
+	     pos = list_entry(pos->member.next, typeof(*pos), member))
+
+/**
+ * list_for_each_entry_safe - iterate over list of given type safe against removal of list entry
+ * @pos:	the type * to use as a loop cursor.
+ * @n:		another type * to use as temporary storage
+ * @head:	the head for your list.
+ * @member:	the name of the list_struct within the struct.
+ */
+#define list_for_each_entry_safe(pos, n, head, member)			\
+	for (pos = list_entry((head)->next, typeof(*pos), member),	\
+		n = list_entry(pos->member.next, typeof(*pos), member);	\
+	     &pos->member != (head); 					\
+	     pos = n, n = list_entry(n->member.next, typeof(*n), member))
+
+/**
+ * list_for_each_entry_safe_continue
+ * @pos:	the type * to use as a loop cursor.
+ * @n:		another type * to use as temporary storage
+ * @head:	the head for your list.
+ * @member:	the name of the list_struct within the struct.
+ *
+ * Iterate over list of given type, continuing after current point,
+ * safe against removal of list entry.
+ */
+#define list_for_each_entry_safe_continue(pos, n, head, member)		\
+	for (pos = list_entry(pos->member.next, typeof(*pos), member), 	\
+		n = list_entry(pos->member.next, typeof(*pos), member);	\
+	     &pos->member != (head);					\
+	     pos = n, n = list_entry(n->member.next, typeof(*n), member))
+
+/**
+ * list_for_each_entry_safe_from
+ * @pos:	the type * to use as a loop cursor.
+ * @n:		another type * to use as temporary storage
+ * @head:	the head for your list.
+ * @member:	the name of the list_struct within the struct.
+ *
+ * Iterate over list of given type from current point, safe against
+ * removal of list entry.
+ */
+#define list_for_each_entry_safe_from(pos, n, head, member) 		\
+	for (n = list_entry(pos->member.next, typeof(*pos), member);	\
+	     &pos->member != (head);					\
+	     pos = n, n = list_entry(n->member.next, typeof(*n), member))
+
+/**
+ * list_for_each_entry_safe_reverse
+ * @pos:	the type * to use as a loop cursor.
+ * @n:		another type * to use as temporary storage
+ * @head:	the head for your list.
+ * @member:	the name of the list_struct within the struct.
+ *
+ * Iterate backwards over list of given type, safe against removal
+ * of list entry.
+ */
+#define list_for_each_entry_safe_reverse(pos, n, head, member)		\
+	for (pos = list_entry((head)->prev, typeof(*pos), member),	\
+		n = list_entry(pos->member.prev, typeof(*pos), member);	\
+	     &pos->member != (head); 					\
+	     pos = n, n = list_entry(n->member.prev, typeof(*n), member))
+
+#endif /* LIST_H */
diff --git a/include/log.h b/include/log.h
new file mode 100644
index 0000000..7a2a138
--- /dev/null
+++ b/include/log.h
@@ -0,0 +1,15 @@
+#ifndef LOG_H
+#define LOG_H
+
+void log_start(char *program, int daemon, int level);
+void log_stop();
+void do_log(int priority, const char *fmt, va_list ap);
+void log_debug(int level, char *fmt, ...);
+void log_warn(char *fmt, ...);
+void log_err(char *fmt, ...);
+void _log_errno(const char *func, char *call, int errnum);
+
+#define log_errno(s) _log_errno(__func__, s, errno)
+
+#endif /* LOG_H */
+
diff --git a/log.c b/log.c
new file mode 100644
index 0000000..a99ab93
--- /dev/null
+++ b/log.c
@@ -0,0 +1,87 @@
+/*
+ * Copyright(c) 2009 Intel Corporation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Maintained at www.Open-FCoE.org
+ */
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <syslog.h>
+#include <string.h>
+
+static int log_syslog;
+static int log_debug_level;
+static char log_prefix[256];
+
+void log_start(char *program, int daemon, int level)
+{
+	log_syslog = daemon;
+	log_debug_level = level;
+	strncpy(log_prefix, program, 256);
+	log_prefix[255] = '\0';
+
+	if (log_syslog)
+		openlog(log_prefix, 0, LOG_DAEMON);
+}
+
+void log_stop()
+{
+	if (log_syslog)
+		closelog();
+}
+
+void do_log(int priority, const char *fmt, va_list ap)
+{
+	if (log_syslog)
+		vsyslog(priority, fmt, ap);
+	else {
+		printf("%s: ", log_prefix);
+		vprintf(fmt, ap);
+		printf("\n");
+	}
+}
+
+void log_debug(int level, char *fmt, ...)
+{
+	va_list ap;
+	if (log_debug_level >= level) {
+		va_start(ap, fmt);
+		do_log(LOG_DEBUG, fmt, ap);
+		va_end(ap);
+	}
+}
+
+void log_warn(char *fmt, ...)
+{
+	va_list ap;
+	va_start(ap, fmt);
+	do_log(LOG_WARNING, fmt, ap);
+	va_end(ap);
+}
+
+void log_err(char *fmt, ...)
+{
+	va_list ap;
+	va_start(ap, fmt);
+	do_log(LOG_ERR, fmt, ap);
+	va_end(ap);
+}
+
+void _log_errno(const char *func, char *call, int errnum)
+{
+	log_err("%s %s: %s", func, call, strerror(errnum));
+}
+




More information about the devel mailing list