diff options
-rw-r--r-- | src/config/config_ethernet.c | 3 | ||||
-rw-r--r-- | src/config/general.h | 1 | ||||
-rw-r--r-- | src/include/ipxe/errfile.h | 1 | ||||
-rw-r--r-- | src/include/ipxe/stp.h | 76 | ||||
-rw-r--r-- | src/net/stp.c | 152 |
5 files changed, 233 insertions, 0 deletions
diff --git a/src/config/config_ethernet.c b/src/config/config_ethernet.c index 372ec993..de7a07c5 100644 --- a/src/config/config_ethernet.c +++ b/src/config/config_ethernet.c @@ -40,3 +40,6 @@ REQUIRE_OBJECT ( aoe ); #ifdef NET_PROTO_FCOE REQUIRE_OBJECT ( fcoe ); #endif +#ifdef NET_PROTO_STP +REQUIRE_OBJECT ( stp ); +#endif diff --git a/src/config/general.h b/src/config/general.h index 7676f158..c8afc10a 100644 --- a/src/config/general.h +++ b/src/config/general.h @@ -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 diff --git a/src/include/ipxe/errfile.h b/src/include/ipxe/errfile.h index d217eea0..97d55573 100644 --- a/src/include/ipxe/errfile.h +++ b/src/include/ipxe/errfile.h @@ -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 ) diff --git a/src/include/ipxe/stp.h b/src/include/ipxe/stp.h new file mode 100644 index 00000000..3d85e5ba --- /dev/null +++ b/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 */ diff --git a/src/net/stp.c b/src/net/stp.c new file mode 100644 index 00000000..cafb6b8e --- /dev/null +++ b/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, +}; |