mirror of
https://github.com/ipxe/ipxe
synced 2025-12-22 13:00:39 +03:00
[fcoe] Add support for the FCoE Initialization Protocol (FIP)
Signed-off-by: Michael Brown <mcb30@ipxe.org>
This commit is contained in:
724
src/net/fcoe.c
724
src/net/fcoe.c
@@ -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,18 +71,75 @@ 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];
|
||||
};
|
||||
|
||||
/** 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 */
|
||||
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 );
|
||||
|
||||
struct net_protocol fcoe_protocol __net_protocol;
|
||||
|
||||
/** Default FCoE forwarded MAC address */
|
||||
uint8_t fcoe_default_fcf_ll_addr[ETH_ALEN] =
|
||||
{ 0x0e, 0xfc, 0x00, 0xff, 0xff, 0xfe };
|
||||
/******************************************************************************
|
||||
*
|
||||
* 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 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 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 );
|
||||
|
||||
/* 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
|
||||
|
||||
Reference in New Issue
Block a user