]> git.ipfire.org Git - thirdparty/ipxe.git/commitdiff
[fcoe] Add support for the FCoE Initialization Protocol (FIP)
authorMichael Brown <mcb30@ipxe.org>
Thu, 7 Oct 2010 15:21:56 +0000 (16:21 +0100)
committerMichael Brown <mcb30@ipxe.org>
Thu, 7 Oct 2010 18:20:36 +0000 (19:20 +0100)
Signed-off-by: Michael Brown <mcb30@ipxe.org>
src/include/ipxe/fcoe.h
src/include/ipxe/fip.h [new file with mode: 0644]
src/net/fcoe.c

index 25723ec7a3153c8dd6597f6937e62844118138e4..69120d3e66d83592a8f8b28dad8946f12723719d 100644 (file)
@@ -33,6 +33,19 @@ union fcoe_name {
 /** IEEE extended */
 #define FCOE_AUTHORITY_IEEE_EXTENDED 0x2000
 
+/** An FCoE MAC address prefix (FC-MAP) */
+struct fcoe_map {
+       uint8_t bytes[3];
+} __attribute__ (( packed ));
+
+/** An FCoE (fabric-assigned) MAC address */
+struct fcoe_mac {
+       /** MAC address prefix */
+       struct fcoe_map map;
+       /** Port ID */
+       struct fc_port_id port_id;
+} __attribute__ (( packed ));
+
 /** An FCoE header */
 struct fcoe_header {
        /** FCoE frame version */
diff --git a/src/include/ipxe/fip.h b/src/include/ipxe/fip.h
new file mode 100644 (file)
index 0000000..b81e860
--- /dev/null
@@ -0,0 +1,450 @@
+#ifndef _IPXE_FIP_H
+#define _IPXE_FIP_H
+
+/*
+ * Copyright (C) 2010 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <stdint.h>
+#include <ipxe/fc.h>
+#include <ipxe/fcels.h>
+#include <ipxe/fcoe.h>
+
+/** A FIP frame header */
+struct fip_header {
+       /** Frame version */
+       uint8_t version;
+       /** Reserved */
+       uint8_t reserved_a;
+       /** Protocol code */
+       uint16_t code;
+       /** Reserved */
+       uint8_t reserved_b;
+       /** Subcode */
+       uint8_t subcode;
+       /** Descriptor list length in 32-bit words */
+       uint16_t len;
+       /** Flags */
+       uint16_t flags;
+} __attribute__ (( packed ));
+
+/** FIP frame version */
+#define FIP_VERSION 0x10
+
+/** FIP protocol code */
+enum fip_code {
+       FIP_CODE_DISCOVERY = 0x0001,    /**< Discovery */
+       FIP_CODE_ELS = 0x0002,          /**< Extended link services */
+       FIP_CODE_MAINTAIN = 0x0003,     /**< Maintain virtual links */
+       FIP_CODE_VLAN = 0x0004,         /**< VLAN */
+};
+
+/** FIP protocol subcode for discovery */
+enum fip_discovery_subcode {
+       FIP_DISCOVERY_SOLICIT = 0x01,   /**< Discovery solicitation */
+       FIP_DISCOVERY_ADVERTISE = 0x02, /**< Discovery advertisement */
+};
+
+/** FIP protocol subcode for extended link services */
+enum fip_els_subcode {
+       FIP_ELS_REQUEST = 0x01,         /**< ELS request */
+       FIP_ELS_RESPONSE = 0x02,        /**< ELS response */
+};
+
+/** FIP protocol subcode for keep alive / clear links */
+enum fip_vitality_subcode {
+       FIP_MAINTAIN_KEEP_ALIVE = 0x01, /**< Keep alive */
+       FIP_MAINTAIN_CLEAR_LINKS = 0x02,/**< Clear virtual links */
+};
+
+/** FIP protocol subcode for VLAN */
+enum fip_vlan_subcode {
+       FIP_VLAN_REQUEST = 0x01,        /**< VLAN request */
+       FIP_VLAN_NOTIFY = 0x02,         /**< VLAN notification */
+};
+
+/** FIP flags */
+enum fip_flags {
+       FIP_FP  = 0x8000,               /**< Fabric-provided MAC address */
+       FIP_SP  = 0x4000,               /**< Server-provided MAC address */
+       FIP_A   = 0x0004,               /**< Available for login */
+       FIP_S   = 0x0002,               /**< Solicited */
+       FIP_F   = 0x0001,               /**< Forwarder */
+};
+
+/** FIP descriptor common fields */
+struct fip_common {
+       /** Type */
+       uint8_t type;
+       /** Length in 32-bit words */
+       uint8_t len;
+       /** Reserved */
+       uint8_t reserved[2];
+} __attribute__ (( packed ));
+
+/** FIP descriptor types */
+enum fip_type {
+       FIP_RESERVED = 0x00,            /**< Reserved */
+       FIP_PRIORITY = 0x01,            /**< Priority */
+       FIP_MAC_ADDRESS = 0x02,         /**< MAC address */
+       FIP_FC_MAP = 0x03,              /**< FC-MAP */
+       FIP_NAME_ID = 0x04,             /**< Name identifier */
+       FIP_FABRIC = 0x05,              /**< Fabric */
+       FIP_MAX_FCOE_SIZE = 0x06,       /**< Max FCoE size */
+       FIP_FLOGI = 0x07,               /**< FLOGI */
+       FIP_NPIV_FDISC = 0x08,          /**< NPIV FDISC */
+       FIP_LOGO = 0x09,                /**< LOGO */
+       FIP_ELP = 0x0a,                 /**< ELP */
+       FIP_VX_PORT_ID = 0x0b,          /**< Vx port identification */
+       FIP_FKA_ADV_P = 0x0c,           /**< FKA ADV period */
+       FIP_VENDOR_ID = 0x0d,           /**< Vendor ID */
+       FIP_VLAN = 0x0e,                /**< VLAN */
+       FIP_NUM_DESCRIPTOR_TYPES
+};
+
+/** FIP descriptor type is critical */
+#define FIP_IS_CRITICAL( type ) ( (type) <= 0x7f )
+
+/** A FIP priority descriptor */
+struct fip_priority {
+       /** Type */
+       uint8_t type;
+       /** Length in 32-bit words */
+       uint8_t len;
+       /** Reserved */
+       uint8_t reserved;
+       /** Priority
+        *
+        * A higher value indicates a lower priority.
+        */
+       uint8_t priority;
+} __attribute__ (( packed ));
+
+/** Default FIP priority */
+#define FIP_DEFAULT_PRIORITY 128
+
+/** Lowest FIP priority */
+#define FIP_LOWEST_PRIORITY 255
+
+/** A FIP MAC address descriptor */
+struct fip_mac_address {
+       /** Type */
+       uint8_t type;
+       /** Length in 32-bit words */
+       uint8_t len;
+       /** MAC address */
+       uint8_t mac[ETH_ALEN];
+} __attribute__ (( packed ));
+
+/** A FIP FC-MAP descriptor */
+struct fip_fc_map {
+       /** Type */
+       uint8_t type;
+       /** Length in 32-bit words */
+       uint8_t len;
+       /** Reserved */
+       uint8_t reserved[3];
+       /** FC-MAP */
+       struct fcoe_map map;
+} __attribute__ (( packed ));
+
+/** A FIP name identifier descriptor */
+struct fip_name_id {
+       /** Type */
+       uint8_t type;
+       /** Length in 32-bit words */
+       uint8_t len;
+       /** Reserved */
+       uint8_t reserved[2];
+       /** Name identifier */
+       struct fc_name name;
+} __attribute__ (( packed ));
+
+/** A FIP fabric descriptor */
+struct fip_fabric {
+       /** Type */
+       uint8_t type;
+       /** Length in 32-bit words */
+       uint8_t len;
+       /** Virtual Fabric ID, if any */
+       uint16_t vf_id;
+       /** Reserved */
+       uint8_t reserved;
+       /** FC-MAP */
+       struct fcoe_map map;
+       /** Fabric name */
+       struct fc_name name;
+} __attribute__ (( packed ));
+
+/** A FIP max FCoE size descriptor */
+struct fip_max_fcoe_size {
+       /** Type */
+       uint8_t type;
+       /** Length in 32-bit words */
+       uint8_t len;
+       /** Maximum FCoE size */
+       uint16_t mtu;
+} __attribute__ (( packed ));
+
+/** A FIP descriptor containing an encapsulated ELS frame */
+struct fip_els {
+       /** Type */
+       uint8_t type;
+       /** Length in 32-bit words */
+       uint8_t len;
+       /** Reserved */
+       uint8_t reserved[2];
+       /** Fibre Channel frame header */
+       struct fc_frame_header fc;
+       /** ELS frame */
+       struct fc_els_frame_common els;
+} __attribute__ (( packed ));
+
+/** A FIP descriptor containing an encapsulated login frame */
+struct fip_login {
+       /** Type */
+       uint8_t type;
+       /** Length in 32-bit words */
+       uint8_t len;
+       /** Reserved */
+       uint8_t reserved[2];
+       /** Fibre Channel frame header */
+       struct fc_frame_header fc;
+       /** ELS frame */
+       struct fc_login_frame els;
+} __attribute__ (( packed ));
+
+/** A FIP descriptor containing an encapsulated LOGO request frame */
+struct fip_logo_request {
+       /** Type */
+       uint8_t type;
+       /** Length in 32-bit words */
+       uint8_t len;
+       /** Reserved */
+       uint8_t reserved[2];
+       /** Fibre Channel frame header */
+       struct fc_frame_header fc;
+       /** ELS frame */
+       struct fc_logout_request_frame els;
+} __attribute__ (( packed ));
+
+/** A FIP descriptor containing an encapsulated LOGO response frame */
+struct fip_logo_response {
+       /** Type */
+       uint8_t type;
+       /** Length in 32-bit words */
+       uint8_t len;
+       /** Reserved */
+       uint8_t reserved[2];
+       /** Fibre Channel frame header */
+       struct fc_frame_header fc;
+       /** ELS frame */
+       struct fc_logout_response_frame els;
+} __attribute__ (( packed ));
+
+/** A FIP descriptor containing an encapsulated ELP frame */
+struct fip_elp {
+       /** Type */
+       uint8_t type;
+       /** Length in 32-bit words */
+       uint8_t len;
+       /** Reserved */
+       uint8_t reserved[2];
+       /** Fibre Channel frame header */
+       struct fc_frame_header fc;
+       /** ELS frame */
+       struct fc_els_frame_common els;
+       /** Uninteresting content */
+       uint32_t dull[25];
+} __attribute__ (( packed ));
+
+/** A FIP descriptor containing an encapsulated LS_RJT frame */
+struct fip_ls_rjt {
+       /** Type */
+       uint8_t type;
+       /** Length in 32-bit words */
+       uint8_t len;
+       /** Reserved */
+       uint8_t reserved[2];
+       /** Fibre Channel frame header */
+       struct fc_frame_header fc;
+       /** ELS frame */
+       struct fc_ls_rjt_frame els;
+} __attribute__ (( packed ));
+
+/** A FIP Vx port identification descriptor */
+struct fip_vx_port_id {
+       /** Type */
+       uint8_t type;
+       /** Length in 32-bit words */
+       uint8_t len;
+       /** MAC address */
+       uint8_t mac[ETH_ALEN];
+       /** Reserved */
+       uint8_t reserved;
+       /** Address identifier */
+       struct fc_port_id id;
+       /** Port name */
+       struct fc_name name;
+} __attribute__ (( packed ));
+
+/** A FIP FKA ADV period descriptor */
+struct fip_fka_adv_p {
+       /** Type */
+       uint8_t type;
+       /** Length in 32-bit words */
+       uint8_t len;
+       /** Reserved */
+       uint8_t reserved;
+       /** Flags */
+       uint8_t flags;
+       /** Keep alive advertisement period in milliseconds */
+       uint32_t period;
+} __attribute__ (( packed ));
+
+/** FIP FKA ADV period flags */
+enum fip_fka_adv_p_flags {
+       FIP_NO_KEEPALIVE = 0x01,        /**< Do not send keepalives */
+};
+
+/** A FIP vendor ID descriptor */
+struct fip_vendor_id {
+       /** Type */
+       uint8_t type;
+       /** Length in 32-bit words */
+       uint8_t len;
+       /** Reserved */
+       uint8_t reserved[2];
+       /** Vendor ID */
+       uint8_t vendor[8];
+} __attribute__ (( packed ));
+
+/** A FIP VLAN descriptor */
+struct fip_vlan {
+       /** Type */
+       uint8_t type;
+       /** Length in 32-bit words */
+       uint8_t len;
+       /** VLAN ID */
+       uint16_t vlan;
+} __attribute__ (( packed ));
+
+/** A FIP descriptor */
+union fip_descriptor {
+       /** Common fields */
+       struct fip_common common;
+       /** Priority descriptor */
+       struct fip_priority priority;
+       /** MAC address descriptor */
+       struct fip_mac_address mac_address;
+       /** FC-MAP descriptor */
+       struct fip_fc_map fc_map;
+       /** Name identifier descriptor */
+       struct fip_name_id name_id;
+       /** Fabric descriptor */
+       struct fip_fabric fabric;
+       /** Max FCoE size descriptor */
+       struct fip_max_fcoe_size max_fcoe_size;
+       /** FLOGI descriptor */
+       struct fip_els flogi;
+       /** FLOGI request descriptor */
+       struct fip_login flogi_request;
+       /** FLOGI LS_ACC descriptor */
+       struct fip_login flogi_ls_acc;
+       /** FLOGI LS_RJT descriptor */
+       struct fip_ls_rjt flogi_ls_rjt;
+       /** NPIV FDISC descriptor */
+       struct fip_els npiv_fdisc;
+       /** NPIV FDISC request descriptor */
+       struct fip_login npiv_fdisc_request;
+       /** NPIV FDISC LS_ACC descriptor */
+       struct fip_login npiv_fdisc_ls_acc;
+       /** NPIV FDISC LS_RJT descriptor */
+       struct fip_ls_rjt npiv_fdisc_ls_rjt;
+       /** LOGO descriptor */
+       struct fip_els logo;
+       /** LOGO request descriptor */
+       struct fip_logo_request logo_request;
+       /** LOGO LS_ACC descriptor */
+       struct fip_logo_response logo_ls_acc;
+       /** LOGO LS_RJT descriptor */
+       struct fip_ls_rjt logo_ls_rjt;
+       /** ELS descriptor */
+       struct fip_els elp;
+       /** ELP request descriptor */
+       struct fip_elp elp_request;
+       /** ELP LS_ACC descriptor */
+       struct fip_elp elp_ls_acc;
+       /** ELP LS_RJT descriptor */
+       struct fip_ls_rjt elp_ls_rjt;
+       /** Vx port identification descriptor */
+       struct fip_vx_port_id vx_port_id;
+       /** FKA ADV period descriptor */
+       struct fip_fka_adv_p fka_adv_p;
+       /** Vendor ID descriptor */
+       struct fip_vendor_id vendor_id;
+       /** VLAN descriptor */
+       struct fip_vlan vlan;
+} __attribute__ (( packed ));
+
+/** A FIP descriptor set */
+struct fip_descriptors {
+       /** Descriptors, indexed by type */
+       union fip_descriptor *desc[FIP_NUM_DESCRIPTOR_TYPES];
+};
+
+/**
+ * Define a function to extract a specific FIP descriptor type from a list
+ *
+ * @v type             Descriptor type
+ * @v name             Descriptor name
+ * @v finder           Descriptor finder
+ */
+#define FIP_DESCRIPTOR( type, name )                                   \
+       static inline __attribute__ (( always_inline ))                 \
+       typeof ( ( ( union fip_descriptor * ) NULL )->name ) *          \
+       fip_ ## name ( struct fip_descriptors *descs ) {                \
+               return &(descs->desc[type]->name);                      \
+       }
+FIP_DESCRIPTOR ( FIP_PRIORITY, priority );
+FIP_DESCRIPTOR ( FIP_MAC_ADDRESS, mac_address );
+FIP_DESCRIPTOR ( FIP_FC_MAP, fc_map );
+FIP_DESCRIPTOR ( FIP_NAME_ID, name_id );
+FIP_DESCRIPTOR ( FIP_FABRIC, fabric );
+FIP_DESCRIPTOR ( FIP_MAX_FCOE_SIZE, max_fcoe_size );
+FIP_DESCRIPTOR ( FIP_FLOGI, flogi );
+FIP_DESCRIPTOR ( FIP_FLOGI, flogi_request );
+FIP_DESCRIPTOR ( FIP_FLOGI, flogi_ls_acc );
+FIP_DESCRIPTOR ( FIP_FLOGI, flogi_ls_rjt );
+FIP_DESCRIPTOR ( FIP_NPIV_FDISC, npiv_fdisc );
+FIP_DESCRIPTOR ( FIP_NPIV_FDISC, npiv_fdisc_request );
+FIP_DESCRIPTOR ( FIP_NPIV_FDISC, npiv_fdisc_ls_acc );
+FIP_DESCRIPTOR ( FIP_NPIV_FDISC, npiv_fdisc_ls_rjt );
+FIP_DESCRIPTOR ( FIP_LOGO, logo );
+FIP_DESCRIPTOR ( FIP_LOGO, logo_request );
+FIP_DESCRIPTOR ( FIP_LOGO, logo_ls_acc );
+FIP_DESCRIPTOR ( FIP_LOGO, logo_ls_rjt );
+FIP_DESCRIPTOR ( FIP_ELP, elp );
+FIP_DESCRIPTOR ( FIP_ELP, elp_request );
+FIP_DESCRIPTOR ( FIP_ELP, elp_ls_acc );
+FIP_DESCRIPTOR ( FIP_ELP, elp_ls_rjt );
+FIP_DESCRIPTOR ( FIP_VX_PORT_ID, vx_port_id );
+FIP_DESCRIPTOR ( FIP_FKA_ADV_P, fka_adv_p );
+FIP_DESCRIPTOR ( FIP_VENDOR_ID, vendor_id );
+FIP_DESCRIPTOR ( FIP_VLAN, vlan );
+
+#endif /* _IPXE_FIP_H */
index 11acfca219bc74395b16987e875be30b9ed0c9cb..f22f9f7be042500129c9a24d5aad0f87f417c13b 100644 (file)
@@ -28,11 +28,15 @@ FILE_LICENCE ( GPL2_OR_LATER );
 #include <ipxe/interface.h>
 #include <ipxe/xfer.h>
 #include <ipxe/netdevice.h>
+#include <ipxe/ethernet.h>
 #include <ipxe/features.h>
 #include <ipxe/errortab.h>
 #include <ipxe/device.h>
 #include <ipxe/crc32.h>
+#include <ipxe/retry.h>
+#include <ipxe/timer.h>
 #include <ipxe/fc.h>
+#include <ipxe/fip.h>
 #include <ipxe/fcoe.h>
 
 /** @file
@@ -67,19 +71,76 @@ struct fcoe_port {
        struct interface transport;
        /** Network device */
        struct net_device *netdev;
+
+       /** Node WWN */
+       union fcoe_name node_wwn;
+       /** Port WWN */
+       union fcoe_name port_wwn;
+
+       /** FIP retransmission timer */
+       struct retry_timer timer;
+       /** FIP timeout counter */
+       unsigned int timeouts;
+       /** Flags */
+       unsigned int flags;
+       /** FCoE forwarder priority */
+       unsigned int priority;
+       /** Keepalive delay (in ms) */
+       unsigned int keepalive;
        /** FCoE forwarder MAC address */
-       uint8_t fcf_ll_addr[ETH_ALEN];
+       uint8_t fcf_mac[ETH_ALEN];
+       /** Local MAC address */
+       uint8_t local_mac[ETH_ALEN];
 };
 
-/** List of FCoE ports */
-static LIST_HEAD ( fcoe_ports );
+/** FCoE flags */
+enum fcoe_flags {
+       /** Underlying network device is available */
+       FCOE_HAVE_NETWORK = 0x0001,
+       /** We have selected an FCoE forwarder to use */
+       FCOE_HAVE_FCF = 0x0002,
+       /** We have a FIP-capable FCoE forwarder available to be used */
+       FCOE_HAVE_FIP_FCF = 0x0004,
+};
 
 struct net_protocol fcoe_protocol __net_protocol;
+struct net_protocol fip_protocol __net_protocol;
+
+/** FCoE All-FCoE-MACs address */
+static uint8_t all_fcoe_macs[ETH_ALEN] =
+       { 0x01, 0x10, 0x18, 0x01, 0x00, 0x00 };
+
+/** FCoE All-ENode-MACs address */
+static uint8_t all_enode_macs[ETH_ALEN] =
+       { 0x01, 0x10, 0x18, 0x01, 0x00, 0x01 };
+
+/** FCoE All-FCF-MACs address */
+static uint8_t all_fcf_macs[ETH_ALEN] =
+       { 0x01, 0x10, 0x18, 0x01, 0x00, 0x02 };
 
 /** Default FCoE forwarded MAC address */
-uint8_t fcoe_default_fcf_ll_addr[ETH_ALEN] =
+static uint8_t default_fcf_mac[ETH_ALEN] =
        { 0x0e, 0xfc, 0x00, 0xff, 0xff, 0xfe };
 
+/** Maximum number of FIP solicitations before giving up on FIP */
+#define FCOE_MAX_FIP_SOLICITATIONS 2
+
+/** Delay between retrying FIP solicitations */
+#define FCOE_FIP_RETRY_DELAY ( TICKS_PER_SEC )
+
+/** Maximum number of missing discovery advertisements */
+#define FCOE_MAX_FIP_MISSING_KEEPALIVES 4
+
+/** List of FCoE ports */
+static LIST_HEAD ( fcoe_ports );
+
+/******************************************************************************
+ *
+ * FCoE protocol
+ *
+ ******************************************************************************
+ */
+
 /**
  * Identify FCoE port by network device
  *
@@ -96,6 +157,37 @@ static struct fcoe_port * fcoe_demux ( struct net_device *netdev ) {
        return NULL;
 }
 
+/**
+ * Reset FCoE port
+ *
+ * @v fcoe             FCoE port
+ */
+static void fcoe_reset ( struct fcoe_port *fcoe ) {
+
+       /* Reset any FIP state */
+       stop_timer ( &fcoe->timer );
+       fcoe->timeouts = 0;
+       fcoe->flags = 0;
+       fcoe->priority = ( FIP_LOWEST_PRIORITY + 1 );
+       fcoe->keepalive = 0;
+       memcpy ( fcoe->fcf_mac, default_fcf_mac,
+                sizeof ( fcoe->fcf_mac ) );
+       memcpy ( fcoe->local_mac, fcoe->netdev->ll_addr,
+                sizeof ( fcoe->local_mac ) );
+
+       /* Start FIP solicitation if network is available */
+       if ( netdev_is_open ( fcoe->netdev ) &&
+            netdev_link_ok ( fcoe->netdev ) ) {
+               fcoe->flags |= FCOE_HAVE_NETWORK;
+               start_timer_nodelay ( &fcoe->timer );
+               DBGC ( fcoe, "FCoE %s starting FIP solicitation\n",
+                      fcoe->netdev->name );
+       }
+
+       /* Send notification of window change */
+       xfer_window_changed ( &fcoe->transport );
+}
+
 /**
  * Transmit FCoE packet
  *
@@ -108,28 +200,78 @@ static int fcoe_deliver ( struct fcoe_port *fcoe,
                          struct io_buffer *iobuf,
                          struct xfer_metadata *meta __unused ) {
        struct fc_frame_header *fchdr = iobuf->data;
+       struct fc_els_frame_common *els = ( iobuf->data + sizeof ( *fchdr ) );
        struct fcoe_header *fcoehdr;
        struct fcoe_footer *fcoeftr;
+       struct fip_header *fiphdr;
+       struct fip_login *fipflogi;
+       struct fip_mac_address *fipmac;
        uint32_t crc;
+       struct net_protocol *net_protocol;
+       void *ll_source;
        int rc;
 
-       /* Calculate CRC */
-       crc = crc32_le ( ~((uint32_t)0), iobuf->data, iob_len ( iobuf ) );
+       /* Send as FIP or FCoE as appropriate */
+       if ( ( fchdr->r_ctl == ( FC_R_CTL_ELS | FC_R_CTL_UNSOL_CTRL ) ) &&
+            ( els->command == FC_ELS_FLOGI ) &&
+            ( fcoe->flags & FCOE_HAVE_FIP_FCF ) ) {
+
+               /* Create FIP FLOGI descriptor */
+               fipflogi = iob_push ( iobuf,
+                                     offsetof ( typeof ( *fipflogi ), fc ) );
+               memset ( fipflogi, 0, offsetof ( typeof ( *fipflogi ), fc ) );
+               fipflogi->type = FIP_FLOGI;
+               fipflogi->len = ( iob_len ( iobuf ) / 4 );
+
+               /* Create FIP MAC address descriptor */
+               fipmac = iob_put ( iobuf, sizeof ( *fipmac ) );
+               memset ( fipmac, 0, sizeof ( *fipmac ) );
+               fipmac->type = FIP_MAC_ADDRESS;
+               fipmac->len = ( sizeof ( *fipmac ) / 4 );
+               memcpy ( fipmac->mac, fcoe->netdev->ll_addr,
+                        sizeof ( fipmac->mac ) );
+
+               /* Create FIP header */
+               fiphdr = iob_push ( iobuf, sizeof ( *fiphdr ) );
+               memset ( fiphdr, 0, sizeof ( *fiphdr ) );
+               fiphdr->version = FIP_VERSION;
+               fiphdr->code = htons ( FIP_CODE_ELS );
+               fiphdr->subcode = FIP_ELS_REQUEST;
+               fiphdr->len =
+                       htons ( ( iob_len ( iobuf ) - sizeof ( *fiphdr ) ) / 4);
+               fiphdr->flags = htons ( FIP_FP | FIP_SP );
+
+               /* Send as FIP packet from netdev's own MAC address */
+               net_protocol = &fip_protocol;
+               ll_source = fcoe->netdev->ll_addr;
+
+       } else {
+
+               /* Calculate CRC */
+               crc = crc32_le ( ~((uint32_t)0), iobuf->data,
+                                iob_len ( iobuf ) );
 
-       /* Create FCoE header */
-       fcoehdr = iob_push ( iobuf, sizeof ( *fcoehdr ) );
-       memset ( fcoehdr, 0, sizeof ( *fcoehdr ) );
-       fcoehdr->sof = ( ( fchdr->seq_cnt == ntohs ( 0 ) ) ?
-                        FCOE_SOF_I3 : FCOE_SOF_N3 );
-       fcoeftr = iob_put ( iobuf, sizeof ( *fcoeftr ) );
-       memset ( fcoeftr, 0, sizeof ( *fcoeftr ) );
-       fcoeftr->crc = cpu_to_le32 ( crc ^ ~((uint32_t)0) );
-       fcoeftr->eof = ( ( fchdr->f_ctl_es & FC_F_CTL_ES_END ) ?
-                        FCOE_EOF_T : FCOE_EOF_N );
+               /* Create FCoE header */
+               fcoehdr = iob_push ( iobuf, sizeof ( *fcoehdr ) );
+               memset ( fcoehdr, 0, sizeof ( *fcoehdr ) );
+               fcoehdr->sof = ( ( fchdr->seq_cnt == ntohs ( 0 ) ) ?
+                                FCOE_SOF_I3 : FCOE_SOF_N3 );
+
+               /* Create FCoE footer */
+               fcoeftr = iob_put ( iobuf, sizeof ( *fcoeftr ) );
+               memset ( fcoeftr, 0, sizeof ( *fcoeftr ) );
+               fcoeftr->crc = cpu_to_le32 ( crc ^ ~((uint32_t)0) );
+               fcoeftr->eof = ( ( fchdr->f_ctl_es & FC_F_CTL_ES_END ) ?
+                                FCOE_EOF_T : FCOE_EOF_N );
+
+               /* Send as FCoE packet from FCoE MAC address */
+               net_protocol = &fcoe_protocol;
+               ll_source = fcoe->local_mac;
+       }
 
        /* Transmit packet */
-       if ( ( rc = net_tx ( iob_disown ( iobuf ), fcoe->netdev, &fcoe_protocol,
-                            fcoe->fcf_ll_addr, fcoe->netdev->ll_addr )) != 0){
+       if ( ( rc = net_tx ( iob_disown ( iobuf ), fcoe->netdev, net_protocol,
+                            fcoe->fcf_mac, ll_source ) ) != 0 ) {
                DBGC ( fcoe, "FCoE %s could not transmit: %s\n",
                       fcoe->netdev->name, strerror ( rc ) );
                goto done;
@@ -169,7 +311,7 @@ static struct io_buffer * fcoe_alloc_iob ( struct fcoe_port *fcoe __unused,
  * @ret rc             Return status code
  */
 static int fcoe_rx ( struct io_buffer *iobuf, struct net_device *netdev,
-                    const void *ll_dest __unused, const void *ll_source ) {
+                    const void *ll_dest, const void *ll_source ) {
        struct fcoe_header *fcoehdr;
        struct fcoe_footer *fcoeftr;
        struct fcoe_port *fcoe;
@@ -183,6 +325,17 @@ static int fcoe_rx ( struct io_buffer *iobuf, struct net_device *netdev,
                goto done;
        }
 
+       /* Discard packets not destined for us */
+       if ( ( memcmp ( fcoe->local_mac, ll_dest,
+                       sizeof ( fcoe->local_mac ) ) != 0 ) &&
+            ( memcmp ( default_fcf_mac, ll_dest,
+                       sizeof ( default_fcf_mac ) ) != 0 ) ) {
+               DBGC2 ( fcoe, "FCoE %s ignoring packet for %s\n",
+                       fcoe->netdev->name, eth_ntoa ( ll_dest ) );
+               rc = -ENOTCONN;
+               goto done;
+       }
+
        /* Sanity check */
        if ( iob_len ( iobuf ) < ( sizeof ( *fcoehdr ) + sizeof ( *fcoeftr ) )){
                DBGC ( fcoe, "FCoE %s received under-length frame (%zd "
@@ -226,8 +379,11 @@ static int fcoe_rx ( struct io_buffer *iobuf, struct net_device *netdev,
                goto done;
        }
 
-       /* Record FCF address */
-       memcpy ( &fcoe->fcf_ll_addr, ll_source, sizeof ( fcoe->fcf_ll_addr ) );
+       /* Record FCF address if applicable */
+       if ( ( fcoe->flags & FCOE_HAVE_FCF ) &&
+            ( ! ( fcoe->flags & FCOE_HAVE_FIP_FCF ) ) ) {
+               memcpy ( &fcoe->fcf_mac, ll_source, sizeof ( fcoe->fcf_mac ) );
+       }
 
        /* Hand off via transport interface */
        if ( ( rc = xfer_deliver_iob ( &fcoe->transport,
@@ -249,10 +405,7 @@ static int fcoe_rx ( struct io_buffer *iobuf, struct net_device *netdev,
  * @ret len            Length of window
  */
 static size_t fcoe_window ( struct fcoe_port *fcoe ) {
-       struct net_device *netdev = fcoe->netdev;
-
-       return ( ( netdev_is_open ( netdev ) && netdev_link_ok ( netdev ) ) ?
-                ~( ( size_t ) 0 ) : 0 );
+       return ( ( fcoe->flags & FCOE_HAVE_FCF ) ? ~( ( size_t ) 0 ) : 0 );
 }
 
 /**
@@ -263,6 +416,7 @@ static size_t fcoe_window ( struct fcoe_port *fcoe ) {
  */
 static void fcoe_close ( struct fcoe_port *fcoe, int rc ) {
 
+       stop_timer ( &fcoe->timer );
        intf_shutdown ( &fcoe->transport, rc );
        netdev_put ( fcoe->netdev );
        list_del ( &fcoe->list );
@@ -293,6 +447,483 @@ static struct interface_operation fcoe_transport_op[] = {
 static struct interface_descriptor fcoe_transport_desc =
        INTF_DESC ( struct fcoe_port, transport, fcoe_transport_op );
 
+/******************************************************************************
+ *
+ * FIP protocol
+ *
+ ******************************************************************************
+ */
+
+/**
+ * Parse FIP packet into descriptor set
+ *
+ * @v fcoe             FCoE port
+ * @v fiphdr           FIP header
+ * @v len              Length of FIP packet
+ * @v descs            Descriptor set to fill in
+ * @ret rc             Return status code
+ */
+static int fcoe_fip_parse ( struct fcoe_port *fcoe, struct fip_header *fiphdr,
+                           size_t len, struct fip_descriptors *descs ) {
+       union fip_descriptor *desc;
+       size_t descs_len;
+       size_t desc_len;
+       size_t desc_offset;
+       unsigned int desc_type;
+
+       /* Check FIP version */
+       if ( fiphdr->version != FIP_VERSION ) {
+               DBGC ( fcoe, "FCoE %s received unsupported FIP version %02x\n",
+                      fcoe->netdev->name, fiphdr->version );
+               return -EINVAL;
+       }
+
+       /* Check length */
+       descs_len = ( ntohs ( fiphdr->len ) * 4 );
+       if ( ( sizeof ( *fiphdr ) + descs_len ) > len ) {
+               DBGC ( fcoe, "FCoE %s received bad descriptor list length\n",
+                      fcoe->netdev->name );
+               return -EINVAL;
+       }
+
+       /* Parse descriptor list */
+       memset ( descs, 0, sizeof ( *descs ) );
+       for ( desc_offset = 0 ;
+             desc_offset <= ( descs_len - sizeof ( desc->common ) ) ;
+             desc_offset += desc_len ) {
+
+               /* Find descriptor and validate length */
+               desc = ( ( ( void * ) ( fiphdr + 1 ) ) + desc_offset );
+               desc_type = desc->common.type;
+               desc_len = ( desc->common.len * 4 );
+               if ( desc_len == 0 ) {
+                       DBGC ( fcoe, "FCoE %s received zero-length "
+                              "descriptor\n", fcoe->netdev->name );
+                       return -EINVAL;
+               }
+               if ( ( desc_offset + desc_len ) > descs_len ) {
+                       DBGC ( fcoe, "FCoE %s descriptor overrun\n",
+                              fcoe->netdev->name );
+                       return -EINVAL;
+               }
+
+               /* Handle descriptors that we understand */
+               if ( ( desc_type > FIP_RESERVED ) &&
+                    ( desc_type < FIP_NUM_DESCRIPTOR_TYPES ) ) {
+                       descs->desc[desc_type] = desc;
+                       continue;
+               }
+
+               /* Abort if we cannot understand a critical descriptor */
+               if ( FIP_IS_CRITICAL ( desc_type ) ) {
+                       DBGC ( fcoe, "FCoE %s cannot understand critical "
+                              "descriptor type %02x\n",
+                              fcoe->netdev->name, desc_type );
+                       return -ENOTSUP;
+               }
+
+               /* Ignore non-critical descriptors that we cannot understand */
+       }
+
+       return 0;
+}
+
+/**
+ * Send FIP discovery solicitation
+ *
+ * @v fcoe             FCoE port
+ * @ret rc             Return status code
+ */
+static int fcoe_fip_tx_solicitation ( struct fcoe_port *fcoe ) {
+       struct io_buffer *iobuf;
+       struct {
+               struct fip_header hdr;
+               struct fip_mac_address mac_address;
+               struct fip_name_id name_id;
+               struct fip_max_fcoe_size max_fcoe_size;
+       } __attribute__ (( packed )) *solicitation;
+       int rc;
+
+       /* Allocate I/O buffer */
+       iobuf = alloc_iob ( MAX_LL_HEADER_LEN + sizeof ( *solicitation ) );
+       if ( ! iobuf )
+               return -ENOMEM;
+       iob_reserve ( iobuf, MAX_LL_HEADER_LEN );
+
+       /* Construct discovery solicitation */
+       solicitation = iob_put ( iobuf, sizeof ( *solicitation ) );
+       memset ( solicitation, 0, sizeof ( *solicitation ) );
+       solicitation->hdr.version = FIP_VERSION;
+       solicitation->hdr.code = htons ( FIP_CODE_DISCOVERY );
+       solicitation->hdr.subcode = FIP_DISCOVERY_SOLICIT;
+       solicitation->hdr.len = htons ( ( sizeof ( *solicitation ) -
+                                         sizeof ( solicitation->hdr ) ) / 4 );
+       solicitation->hdr.flags = htons ( FIP_FP | FIP_SP );
+       solicitation->mac_address.type = FIP_MAC_ADDRESS;
+       solicitation->mac_address.len =
+               ( sizeof ( solicitation->mac_address ) / 4 );
+       memcpy ( solicitation->mac_address.mac, fcoe->netdev->ll_addr,
+                sizeof ( solicitation->mac_address.mac ) );
+       solicitation->name_id.type = FIP_NAME_ID;
+       solicitation->name_id.len = ( sizeof ( solicitation->name_id ) / 4 );
+       memcpy ( &solicitation->name_id.name, &fcoe->node_wwn.fc,
+                sizeof ( solicitation->name_id.name ) );
+       solicitation->max_fcoe_size.type = FIP_MAX_FCOE_SIZE;
+       solicitation->max_fcoe_size.len =
+               ( sizeof ( solicitation->max_fcoe_size ) / 4 );
+       solicitation->max_fcoe_size.mtu =
+               htons ( ETH_MAX_MTU - sizeof ( struct fcoe_header ) -
+                       sizeof ( struct fcoe_footer ) );
+
+       /* Send discovery solicitation */
+       if ( ( rc = net_tx ( iob_disown ( iobuf ), fcoe->netdev,
+                            &fip_protocol, all_fcf_macs,
+                            fcoe->netdev->ll_addr ) ) != 0 ) {
+               DBGC ( fcoe, "FCoE %s could not send discovery solicitation: "
+                      "%s\n", fcoe->netdev->name, strerror ( rc ) );
+               return rc;
+       }
+
+       return 0;
+}
+
+/**
+ * Handle received FIP discovery advertisement
+ *
+ * @v fcoe             FCoE port
+ * @v descs            Descriptor list
+ * @v flags            Flags
+ * @ret rc             Return status code
+ */
+static int fcoe_fip_rx_advertisement ( struct fcoe_port *fcoe,
+                                      struct fip_descriptors *descs,
+                                      unsigned int flags ) {
+       struct fip_priority *priority = fip_priority ( descs );
+       struct fip_mac_address *mac_address = fip_mac_address ( descs );
+       struct fip_fka_adv_p *fka_adv_p = fip_fka_adv_p ( descs );
+
+       /* Sanity checks */
+       if ( ! priority ) {
+               DBGC ( fcoe, "FCoE %s received advertisement missing "
+                      "priority\n", fcoe->netdev->name );
+               return -EINVAL;
+       }
+       if ( ! mac_address ) {
+               DBGC ( fcoe, "FCoE %s received advertisement missing MAC "
+                      "address\n", fcoe->netdev->name );
+               return -EINVAL;
+       }
+       if ( ! fka_adv_p ) {
+               DBGC ( fcoe, "FCoE %s received advertisement missing FKA ADV "
+                      "period\n", fcoe->netdev->name );
+               return -EINVAL;
+       }
+
+       if ( ! ( fcoe->flags & FCOE_HAVE_FCF ) ) {
+
+               /* We are soliciting for an FCF.  Store the highest
+                * (i.e. lowest-valued) priority solicited
+                * advertisement that we receive.
+                */
+               if ( ( ( flags & ( FIP_A | FIP_S | FIP_F ) ) ==
+                      ( FIP_A | FIP_S | FIP_F ) ) &&
+                    ( priority->priority < fcoe->priority ) ) {
+
+                       fcoe->flags |= FCOE_HAVE_FIP_FCF;
+                       fcoe->priority = priority->priority;
+                       if ( fka_adv_p->flags & FIP_NO_KEEPALIVE ) {
+                               fcoe->keepalive = 0;
+                       } else {
+                               fcoe->keepalive = ntohl ( fka_adv_p->period );
+                       }
+                       memcpy ( fcoe->fcf_mac, mac_address->mac,
+                                sizeof ( fcoe->fcf_mac ) );
+                       DBGC ( fcoe, "FCoE %s selected FCF %s (priority %d, ",
+                              fcoe->netdev->name, eth_ntoa ( fcoe->fcf_mac ),
+                              fcoe->priority );
+                       if ( fcoe->keepalive ) {
+                               DBGC ( fcoe, "keepalive %dms)\n",
+                                      fcoe->keepalive );
+                       } else {
+                               DBGC ( fcoe, "no keepalive)\n" );
+                       }
+               }
+
+       } else if ( fcoe->flags & FCOE_HAVE_FIP_FCF ) {
+
+               /* We are checking that the FCF remains alive.  Reset
+                * the timeout counter if this is an advertisement
+                * from our forwarder.
+                */
+               if ( memcmp ( fcoe->fcf_mac, mac_address->mac,
+                             sizeof ( fcoe->fcf_mac ) ) == 0 ) {
+                       fcoe->timeouts = 0;
+               }
+
+       } else {
+
+               /* We are operating in non-FIP mode and have received
+                * a FIP advertisement.  Reset the link in order to
+                * attempt FIP.
+                */
+               fcoe_reset ( fcoe );
+
+       }
+
+       return 0;
+}
+
+/**
+ * Handle received FIP ELS response
+ *
+ * @v fcoe             FCoE port
+ * @v descs            Descriptor list
+ * @v flags            Flags
+ * @ret rc             Return status code
+ */
+static int fcoe_fip_rx_els_response ( struct fcoe_port *fcoe,
+                                     struct fip_descriptors *descs,
+                                     unsigned int flags __unused ) {
+       struct fip_els *flogi = fip_flogi ( descs );
+       struct fip_mac_address *mac_address = fip_mac_address ( descs );
+       void *frame;
+       size_t frame_len;
+       int rc;
+
+       /* Sanity checks */
+       if ( ! flogi ) {
+               DBGC ( fcoe, "FCoE %s received ELS response missing FLOGI\n",
+                      fcoe->netdev->name );
+               return -EINVAL;
+       }
+       if ( ! mac_address ) {
+               DBGC ( fcoe, "FCoE %s received ELS response missing MAC "
+                      "address\n", fcoe->netdev->name );
+               return -EINVAL;
+       }
+
+       /* Record local MAC address */
+       memcpy ( fcoe->local_mac, mac_address->mac, sizeof ( fcoe->local_mac ));
+
+       /* Hand off via transport interface */
+       frame = &flogi->fc;
+       frame_len = ( ( flogi->len * 4 ) - offsetof ( typeof ( *flogi ), fc ) );
+       if ( ( rc = xfer_deliver_raw ( &fcoe->transport, frame,
+                                      frame_len ) ) != 0 ) {
+               DBGC ( fcoe, "FCoE %s could not deliver FIP FLOGI frame: %s\n",
+                      fcoe->netdev->name, strerror ( rc ) );
+               return rc;
+       }
+
+       return 0;
+}
+
+/**
+ * Send FIP keepalive
+ *
+ * @v fcoe             FCoE port
+ * @ret rc             Return status code
+ */
+static int fcoe_fip_tx_keepalive ( struct fcoe_port *fcoe ) {
+       struct io_buffer *iobuf;
+       struct {
+               struct fip_header hdr;
+               struct fip_mac_address mac_address;
+       } __attribute__ (( packed )) *keepalive;
+       int rc;
+
+       /* Allocate I/O buffer */
+       iobuf = alloc_iob ( MAX_LL_HEADER_LEN + sizeof ( *keepalive ) );
+       if ( ! iobuf )
+               return -ENOMEM;
+       iob_reserve ( iobuf, MAX_LL_HEADER_LEN );
+
+       /* Construct keepalive */
+       keepalive = iob_put ( iobuf, sizeof ( *keepalive ) );
+       memset ( keepalive, 0, sizeof ( *keepalive ) );
+       keepalive->hdr.version = FIP_VERSION;
+       keepalive->hdr.code = htons ( FIP_CODE_MAINTAIN );
+       keepalive->hdr.subcode = FIP_MAINTAIN_KEEP_ALIVE;
+       keepalive->hdr.len =    htons ( ( sizeof ( *keepalive ) -
+                                         sizeof ( keepalive->hdr ) ) / 4 );
+       keepalive->mac_address.type = FIP_MAC_ADDRESS;
+       keepalive->mac_address.len =
+               ( sizeof ( keepalive->mac_address ) / 4 );
+       memcpy ( keepalive->mac_address.mac, fcoe->netdev->ll_addr,
+                sizeof ( keepalive->mac_address.mac ) );
+
+       /* Send keepalive */
+       if ( ( rc = net_tx ( iob_disown ( iobuf ), fcoe->netdev,
+                            &fip_protocol, fcoe->fcf_mac,
+                            fcoe->netdev->ll_addr ) ) != 0 ) {
+               DBGC ( fcoe, "FCoE %s could not send keepalive: %s\n",
+                      fcoe->netdev->name, strerror ( rc ) );
+               return rc;
+       }
+
+       return 0;
+}
+
+/** A FIP handler */
+struct fip_handler {
+       /** Protocol code */
+       uint16_t code;
+       /** Protocol subcode */
+       uint8_t subcode;
+       /**
+        * Receive FIP packet
+        *
+        * @v fcoe              FCoE port
+        * @v descs             Descriptor list
+        * @v flags             Flags
+        * @ret rc              Return status code
+        */
+       int ( * rx ) ( struct fcoe_port *fcoe, struct fip_descriptors *descs,
+                      unsigned int flags );
+};
+
+/** FIP handlers */
+static struct fip_handler fip_handlers[] = {
+       { FIP_CODE_DISCOVERY, FIP_DISCOVERY_ADVERTISE,
+         fcoe_fip_rx_advertisement },
+       { FIP_CODE_ELS, FIP_ELS_RESPONSE,
+         fcoe_fip_rx_els_response },
+};
+
+/**
+ * Process incoming FIP packets
+ *
+ * @v iobuf            I/O buffer
+ * @v netdev           Network device
+ * @v ll_dest          Link-layer destination address
+ * @v ll_source                Link-layer source address
+ * @ret rc             Return status code
+ */
+static int fcoe_fip_rx ( struct io_buffer *iobuf,
+                        struct net_device *netdev,
+                        const void *ll_dest,
+                        const void *ll_source __unused ) {
+       struct fip_header *fiphdr = iobuf->data;
+       struct fip_descriptors descs;
+       struct fip_handler *handler;
+       struct fcoe_port *fcoe;
+       unsigned int i;
+       int rc;
+
+       /* Identify FCoE port */
+       if ( ( fcoe = fcoe_demux ( netdev ) ) == NULL ) {
+               DBG ( "FCoE received FIP frame for net device %s missing FCoE "
+                     "port\n", netdev->name );
+               rc = -ENOTCONN;
+               goto done;
+       }
+
+       /* Discard packets not destined for us */
+       if ( ( memcmp ( fcoe->netdev->ll_addr, ll_dest, ETH_ALEN ) != 0 ) &&
+            ( memcmp ( all_fcoe_macs, ll_dest,
+                       sizeof ( all_fcoe_macs ) ) != 0 ) &&
+            ( memcmp ( all_enode_macs, ll_dest,
+                       sizeof ( all_enode_macs ) ) != 0 ) ) {
+               DBGC2 ( fcoe, "FCoE %s ignoring FIP packet for %s\n",
+                       fcoe->netdev->name, eth_ntoa ( ll_dest ) );
+               rc = -ENOTCONN;
+               goto done;
+       }
+
+       /* Parse FIP packet */
+       if ( ( rc = fcoe_fip_parse ( fcoe, fiphdr, iob_len ( iobuf ),
+                                    &descs ) ) != 0 )
+               goto done;
+
+       /* Find a suitable handler */
+       for ( i = 0 ; i < ( sizeof ( fip_handlers ) /
+                           sizeof ( fip_handlers[0] ) ) ; i++ ) {
+               handler = &fip_handlers[i];
+               if ( ( handler->code == ntohs ( fiphdr->code ) ) &&
+                    ( handler->subcode == fiphdr->subcode ) ) {
+                       rc = handler->rx ( fcoe, &descs,
+                                          ntohs ( fiphdr->flags ) );
+                       goto done;
+               }
+       }
+       DBGC ( fcoe, "FCoE %s received unsupported FIP code %04x.%02x\n",
+              fcoe->netdev->name, ntohs ( fiphdr->code ), fiphdr->subcode );
+       rc = -ENOTSUP;
+
+ done:
+       free_iob ( iobuf );
+       return rc;
+}
+
+/******************************************************************************
+ *
+ * FCoE ports
+ *
+ ******************************************************************************
+ */
+
+/**
+ * Handle FCoE timer expiry
+ *
+ * @v timer            FIP timer
+ * @v over             Timer expired
+ */
+static void fcoe_expired ( struct retry_timer *timer, int over __unused ) {
+       struct fcoe_port *fcoe =
+               container_of ( timer, struct fcoe_port, timer );
+
+       /* Sanity check */
+       assert ( fcoe->flags & FCOE_HAVE_NETWORK );
+
+       /* Increment the timeout counter */
+       fcoe->timeouts++;
+
+       if ( ! ( fcoe->flags & FCOE_HAVE_FCF ) ) {
+
+               /* If we have not yet found a FIP-capable forwarder,
+                * and we have not yet timed out and given up on
+                * finding one, then send a FIP solicitation and wait.
+                */
+               if ( ( ! ( fcoe->flags & FCOE_HAVE_FIP_FCF ) ) &&
+                    ( fcoe->timeouts <= FCOE_MAX_FIP_SOLICITATIONS ) ) {
+                       start_timer_fixed ( &fcoe->timer,
+                                           FCOE_FIP_RETRY_DELAY );
+                       fcoe_fip_tx_solicitation ( fcoe );
+                       return;
+               }
+
+               /* Either we have found a FIP-capable forwarder, or we
+                * have timed out and will fall back to pre-FIP mode.
+                */
+               fcoe->flags |= FCOE_HAVE_FCF;
+               fcoe->timeouts = 0;
+               DBGC ( fcoe, "FCoE %s using %sFIP FCF %s\n", fcoe->netdev->name,
+                      ( ( fcoe->flags & FCOE_HAVE_FIP_FCF ) ? "" : "non-" ),
+                      eth_ntoa ( fcoe->fcf_mac ) );
+
+               /* Start sending keepalives if applicable */
+               if ( fcoe->keepalive )
+                       start_timer_nodelay ( &fcoe->timer );
+
+               /* Send notification of window change */
+               xfer_window_changed ( &fcoe->transport );
+
+       } else {
+
+               /* Send keepalive */
+               start_timer_fixed ( &fcoe->timer,
+                             ( ( fcoe->keepalive * TICKS_PER_SEC ) / 1000 ) );
+               fcoe_fip_tx_keepalive ( fcoe );
+
+               /* Abandon FCF if we have not seen its advertisements */
+               if ( fcoe->timeouts > FCOE_MAX_FIP_MISSING_KEEPALIVES ) {
+                       DBGC ( fcoe, "FCoE %s abandoning FCF %s\n",
+                              fcoe->netdev->name, eth_ntoa ( fcoe->fcf_mac ));
+                       fcoe_reset ( fcoe );
+               }
+       }
+}
+
 /**
  * Create FCoE port
  *
@@ -302,8 +933,6 @@ static struct interface_descriptor fcoe_transport_desc =
 static int fcoe_probe ( struct net_device *netdev ) {
        struct ll_protocol *ll_protocol = netdev->ll_protocol;
        struct fcoe_port *fcoe;
-       union fcoe_name node_wwn;
-       union fcoe_name port_wwn;
        int rc;
 
        /* Sanity check */
@@ -313,7 +942,6 @@ static int fcoe_probe ( struct net_device *netdev ) {
                rc = 0;
                goto err_non_ethernet;
        }
-       assert ( ll_protocol->ll_addr_len == sizeof ( fcoe->fcf_ll_addr ) );
 
        /* Allocate and initialise structure */
        fcoe = zalloc ( sizeof ( *fcoe ) );
@@ -323,27 +951,24 @@ static int fcoe_probe ( struct net_device *netdev ) {
        }
        ref_init ( &fcoe->refcnt, NULL );
        intf_init ( &fcoe->transport, &fcoe_transport_desc, &fcoe->refcnt );
+       timer_init ( &fcoe->timer, fcoe_expired, &fcoe->refcnt );
        fcoe->netdev = netdev_get ( netdev );
 
        /* Construct node and port names */
-       node_wwn.fcoe.authority = htons ( FCOE_AUTHORITY_IEEE );
-       memcpy ( &node_wwn.fcoe.mac, netdev->ll_addr,
-                sizeof ( node_wwn.fcoe.mac ) );
-       port_wwn.fcoe.authority = htons ( FCOE_AUTHORITY_IEEE_EXTENDED );
-       memcpy ( &port_wwn.fcoe.mac, netdev->ll_addr,
-                sizeof ( port_wwn.fcoe.mac ) );
-
-       /* Construct initial FCF address */
-       memcpy ( &fcoe->fcf_ll_addr, &fcoe_default_fcf_ll_addr,
-                sizeof ( fcoe->fcf_ll_addr ) );
+       fcoe->node_wwn.fcoe.authority = htons ( FCOE_AUTHORITY_IEEE );
+       memcpy ( &fcoe->node_wwn.fcoe.mac, netdev->ll_addr,
+                sizeof ( fcoe->node_wwn.fcoe.mac ) );
+       fcoe->port_wwn.fcoe.authority = htons ( FCOE_AUTHORITY_IEEE_EXTENDED );
+       memcpy ( &fcoe->port_wwn.fcoe.mac, netdev->ll_addr,
+                sizeof ( fcoe->port_wwn.fcoe.mac ) );
 
        DBGC ( fcoe, "FCoE %s is %s", fcoe->netdev->name,
-              fc_ntoa ( &node_wwn.fc ) );
-       DBGC ( fcoe, " port %s\n", fc_ntoa ( &port_wwn.fc ) );
+              fc_ntoa ( &fcoe->node_wwn.fc ) );
+       DBGC ( fcoe, " port %s\n", fc_ntoa ( &fcoe->port_wwn.fc ) );
 
        /* Attach Fibre Channel port */
-       if ( ( rc = fc_port_open ( &fcoe->transport, &node_wwn.fc,
-                                  &port_wwn.fc ) ) != 0 )
+       if ( ( rc = fc_port_open ( &fcoe->transport, &fcoe->node_wwn.fc,
+                                  &fcoe->port_wwn.fc ) ) != 0 )
                goto err_fc_create;
 
        /* Transfer reference to port list */
@@ -372,8 +997,8 @@ static void fcoe_notify ( struct net_device *netdev ) {
                return;
        }
 
-       /* Send notification of potential window change */
-       xfer_window_changed ( &fcoe->transport );
+       /* Reset the FCoE link */
+       fcoe_reset ( fcoe );
 }
 
 /**
@@ -410,6 +1035,13 @@ struct net_protocol fcoe_protocol __net_protocol = {
        .rx = fcoe_rx,
 };
 
+/** FIP protocol */
+struct net_protocol fip_protocol __net_protocol = {
+       .name = "FIP",
+       .net_proto = htons ( ETH_P_FIP ),
+       .rx = fcoe_fip_rx,
+};
+
 /** Human-readable message for CRC errors
  *
  * It seems as though several drivers neglect to strip the Ethernet