Skip to content

Commit

Permalink
[stp] Add support for detecting Spanning Tree Protocol non-forwarding…
Browse files Browse the repository at this point in the history
… ports

A fairly common end-user problem is that the default configuration of
a switch may leave the port in a non-forwarding state for a
substantial length of time (tens of seconds) after link up.  This can
cause iPXE to time out and give up attempting to boot.

We cannot force the switch to start forwarding packets sooner, since
any attempt to send a Spanning Tree Protocol bridge PDU may cause the
switch to disable our port (if the switch happens to have the Bridge
PDU Guard feature enabled for the port).

For non-ancient versions of the Spanning Tree Protocol, we can detect
whether or not the port is currently forwarding and use this to inform
the network device core that the link is currently blocked.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
  • Loading branch information
mcb30 committed Jun 25, 2015
1 parent f381239 commit fb28c4a
Show file tree
Hide file tree
Showing 5 changed files with 233 additions and 0 deletions.
3 changes: 3 additions & 0 deletions src/config/config_ethernet.c
Expand Up @@ -40,3 +40,6 @@ REQUIRE_OBJECT ( aoe );
#ifdef NET_PROTO_FCOE
REQUIRE_OBJECT ( fcoe );
#endif
#ifdef NET_PROTO_STP
REQUIRE_OBJECT ( stp );
#endif
1 change: 1 addition & 0 deletions src/config/general.h
Expand Up @@ -37,6 +37,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#define NET_PROTO_IPV4 /* IPv4 protocol */
#undef NET_PROTO_IPV6 /* IPv6 protocol */
#undef NET_PROTO_FCOE /* Fibre Channel over Ethernet protocol */
#define NET_PROTO_STP /* Spanning Tree protocol */

/*
* PXE support
Expand Down
1 change: 1 addition & 0 deletions src/include/ipxe/errfile.h
Expand Up @@ -243,6 +243,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#define ERRFILE_nfs_uri ( ERRFILE_NET | 0x003c0000 )
#define ERRFILE_rndis ( ERRFILE_NET | 0x003d0000 )
#define ERRFILE_pccrc ( ERRFILE_NET | 0x003e0000 )
#define ERRFILE_stp ( ERRFILE_NET | 0x003f0000 )

#define ERRFILE_image ( ERRFILE_IMAGE | 0x00000000 )
#define ERRFILE_elf ( ERRFILE_IMAGE | 0x00010000 )
Expand Down
76 changes: 76 additions & 0 deletions src/include/ipxe/stp.h
@@ -0,0 +1,76 @@
#ifndef _IPXE_STP_H
#define _IPXE_STP_H

/** @file
*
* Spanning Tree Protocol (STP)
*
*/

FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );

#include <stdint.h>
#include <ipxe/if_ether.h>

/** "Protocol" value for STP
*
* This is the concatenated {DSAP,SSAP} value used internally by iPXE
* as the network-layer protocol for LLC frames.
*/
#define ETH_P_STP 0x4242

/** A switch identifier */
struct stp_switch {
/** Priotity */
uint16_t priority;
/** MAC address */
uint8_t mac[ETH_ALEN];
} __attribute__ (( packed ));

/** A Spanning Tree bridge protocol data unit */
struct stp_bpdu {
/** LLC DSAP */
uint8_t dsap;
/** LLC SSAP */
uint8_t ssap;
/** LLC control field */
uint8_t control;
/** Protocol ID */
uint16_t protocol;
/** Protocol version */
uint8_t version;
/** Message type */
uint8_t type;
/** Flags */
uint8_t flags;
/** Root switch */
struct stp_switch root;
/** Root path cost */
uint32_t cost;
/** Sender switch */
struct stp_switch sender;
/** Port */
uint16_t port;
/** Message age */
uint16_t age;
/** Maximum age */
uint16_t max;
/** Hello time */
uint16_t hello;
/** Forward delay */
uint16_t delay;
} __attribute__ (( packed ));

/** Spanning Tree protocol ID */
#define STP_PROTOCOL 0x0000

/** Rapid Spanning Tree protocol version */
#define STP_VERSION_RSTP 0x02

/** Rapid Spanning Tree bridge PDU type */
#define STP_TYPE_RSTP 0x02

/** Port is forwarding */
#define STP_FL_FORWARDING 0x20

#endif /* _IPXE_STP_H */
152 changes: 152 additions & 0 deletions src/net/stp.c
@@ -0,0 +1,152 @@
/*
* Copyright (C) 2015 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 (at your option) 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., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*
* You can also choose to distribute this program under the terms of
* the Unmodified Binary Distribution Licence (as given in the file
* COPYING.UBDL), provided that you have satisfied its requirements.
*/

FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );

#include <errno.h>
#include <byteswap.h>
#include <ipxe/netdevice.h>
#include <ipxe/ethernet.h>
#include <ipxe/iobuf.h>
#include <ipxe/timer.h>
#include <ipxe/stp.h>

/** @file
*
* Spanning Tree Protocol (STP)
*
*/

/* Disambiguate the various error causes */
#define ENOTSUP_PROTOCOL __einfo_error ( EINFO_ENOTSUP_PROTOCOL )
#define EINFO_ENOTSUP_PROTOCOL \
__einfo_uniqify ( EINFO_ENOTSUP, 0x01, \
"Non-STP packet received" )
#define ENOTSUP_VERSION __einfo_error ( EINFO_ENOTSUP_VERSION )
#define EINFO_ENOTSUP_VERSION \
__einfo_uniqify ( EINFO_ENOTSUP, 0x01, \
"Legacy STP packet received" )
#define ENOTSUP_TYPE __einfo_error ( EINFO_ENOTSUP_TYPE )
#define EINFO_ENOTSUP_TYPE \
__einfo_uniqify ( EINFO_ENOTSUP, 0x01, \
"Non-RSTP packet received" )

/**
* Process incoming STP packets
*
* @v iobuf I/O buffer
* @v netdev Network device
* @v ll_source Link-layer source address
* @v flags Packet flags
* @ret rc Return status code
*/
static int stp_rx ( struct io_buffer *iobuf, struct net_device *netdev,
const void *ll_dest __unused,
const void *ll_source __unused,
unsigned int flags __unused ) {
struct stp_bpdu *stp;
unsigned int timeout;
int rc;

/* Sanity check */
if ( iob_len ( iobuf ) < sizeof ( *stp ) ) {
DBGC ( netdev, "STP %s received underlength packet (%zd "
"bytes):\n", netdev->name, iob_len ( iobuf ) );
DBGC_HDA ( netdev, 0, iobuf->data, iob_len ( iobuf ) );
rc = -EINVAL;
goto done;
}
stp = iobuf->data;

/* Ignore non-RSTP packets */
if ( stp->protocol != htons ( STP_PROTOCOL ) ) {
DBGC ( netdev, "STP %s ignoring non-STP packet (protocol "
"%#04x)\n", netdev->name, ntohs ( stp->protocol ) );
rc = -ENOTSUP_PROTOCOL;
goto done;
}
if ( stp->version < STP_VERSION_RSTP ) {
DBGC ( netdev, "STP %s received legacy STP packet (version "
"%#02x)\n", netdev->name, stp->version );
rc = -ENOTSUP_VERSION;
goto done;
}
if ( stp->type != STP_TYPE_RSTP ) {
DBGC ( netdev, "STP %s received non-RSTP packet (type %#02x)\n",
netdev->name, stp->type );
rc = -ENOTSUP_TYPE;
goto done;
}

/* Dump information */
DBGC2 ( netdev, "STP %s %s port %#04x flags %#02x hello %d delay %d\n",
netdev->name, eth_ntoa ( stp->sender.mac ), ntohs ( stp->port ),
stp->flags, ntohs ( stp->hello ), ntohs ( stp->delay ) );

/* Check if port is forwarding */
if ( ! ( stp->flags & STP_FL_FORWARDING ) ) {
/* Port is not forwarding: block link for two hello times */
DBGC ( netdev, "STP %s %s port %#04x flags %#02x is not "
"forwarding\n",
netdev->name, eth_ntoa ( stp->sender.mac ),
ntohs ( stp->port ), stp->flags );
timeout = ( ntohs ( stp->hello ) * TICKS_PER_SEC * 2 );
netdev_link_block ( netdev, timeout );
rc = -ENETUNREACH;
goto done;
}

/* Success */
if ( netdev_link_blocked ( netdev ) ) {
DBGC ( netdev, "STP %s %s port %#04x flags %#02x is "
"forwarding\n",
netdev->name, eth_ntoa ( stp->sender.mac ),
ntohs ( stp->port ), stp->flags );
}
netdev_link_unblock ( netdev );
rc = 0;

done:
free_iob ( iobuf );
return rc;
}

/**
* Transcribe STP address
*
* @v net_addr STP address
* @ret string "<STP>"
*
* This operation is meaningless for the STP protocol.
*/
static const char * stp_ntoa ( const void *net_addr __unused ) {
return "<STP>";
}

/** STP network protocol */
struct net_protocol stp_protocol __net_protocol = {
.name = "STP",
.net_proto = htons ( ETH_P_STP ),
.rx = stp_rx,
.ntoa = stp_ntoa,
};

0 comments on commit fb28c4a

Please sign in to comment.