Browse Source

[stp] Add support for detecting Spanning Tree Protocol non-forwarding 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>
tags/v1.20.1
Michael Brown 9 years ago
parent
commit
fb28c4a979
5 changed files with 233 additions and 0 deletions
  1. 3
    0
      src/config/config_ethernet.c
  2. 1
    0
      src/config/general.h
  3. 1
    0
      src/include/ipxe/errfile.h
  4. 76
    0
      src/include/ipxe/stp.h
  5. 152
    0
      src/net/stp.c

+ 3
- 0
src/config/config_ethernet.c View File

@@ -40,3 +40,6 @@ REQUIRE_OBJECT ( aoe );
40 40
 #ifdef NET_PROTO_FCOE
41 41
 REQUIRE_OBJECT ( fcoe );
42 42
 #endif
43
+#ifdef NET_PROTO_STP
44
+REQUIRE_OBJECT ( stp );
45
+#endif

+ 1
- 0
src/config/general.h View File

@@ -37,6 +37,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
37 37
 #define	NET_PROTO_IPV4		/* IPv4 protocol */
38 38
 #undef	NET_PROTO_IPV6		/* IPv6 protocol */
39 39
 #undef	NET_PROTO_FCOE		/* Fibre Channel over Ethernet protocol */
40
+#define	NET_PROTO_STP		/* Spanning Tree protocol */
40 41
 
41 42
 /*
42 43
  * PXE support

+ 1
- 0
src/include/ipxe/errfile.h View File

@@ -243,6 +243,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
243 243
 #define ERRFILE_nfs_uri			( ERRFILE_NET | 0x003c0000 )
244 244
 #define ERRFILE_rndis			( ERRFILE_NET | 0x003d0000 )
245 245
 #define ERRFILE_pccrc			( ERRFILE_NET | 0x003e0000 )
246
+#define ERRFILE_stp			( ERRFILE_NET | 0x003f0000 )
246 247
 
247 248
 #define ERRFILE_image		      ( ERRFILE_IMAGE | 0x00000000 )
248 249
 #define ERRFILE_elf		      ( ERRFILE_IMAGE | 0x00010000 )

+ 76
- 0
src/include/ipxe/stp.h View File

@@ -0,0 +1,76 @@
1
+#ifndef _IPXE_STP_H
2
+#define _IPXE_STP_H
3
+
4
+/** @file
5
+ *
6
+ * Spanning Tree Protocol (STP)
7
+ *
8
+ */
9
+
10
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
11
+
12
+#include <stdint.h>
13
+#include <ipxe/if_ether.h>
14
+
15
+/** "Protocol" value for STP
16
+ *
17
+ * This is the concatenated {DSAP,SSAP} value used internally by iPXE
18
+ * as the network-layer protocol for LLC frames.
19
+ */
20
+#define ETH_P_STP 0x4242
21
+
22
+/** A switch identifier */
23
+struct stp_switch {
24
+	/** Priotity */
25
+	uint16_t priority;
26
+	/** MAC address */
27
+	uint8_t mac[ETH_ALEN];
28
+} __attribute__ (( packed ));
29
+
30
+/** A Spanning Tree bridge protocol data unit */
31
+struct stp_bpdu {
32
+	/** LLC DSAP */
33
+	uint8_t dsap;
34
+	/** LLC SSAP */
35
+	uint8_t ssap;
36
+	/** LLC control field */
37
+	uint8_t control;
38
+	/** Protocol ID */
39
+	uint16_t protocol;
40
+	/** Protocol version */
41
+	uint8_t version;
42
+	/** Message type */
43
+	uint8_t type;
44
+	/** Flags */
45
+	uint8_t flags;
46
+	/** Root switch */
47
+	struct stp_switch root;
48
+	/** Root path cost */
49
+	uint32_t cost;
50
+	/** Sender switch */
51
+	struct stp_switch sender;
52
+	/** Port */
53
+	uint16_t port;
54
+	/** Message age */
55
+	uint16_t age;
56
+	/** Maximum age */
57
+	uint16_t max;
58
+	/** Hello time */
59
+	uint16_t hello;
60
+	/** Forward delay */
61
+	uint16_t delay;
62
+} __attribute__ (( packed ));
63
+
64
+/** Spanning Tree protocol ID */
65
+#define STP_PROTOCOL 0x0000
66
+
67
+/** Rapid Spanning Tree protocol version */
68
+#define STP_VERSION_RSTP 0x02
69
+
70
+/** Rapid Spanning Tree bridge PDU type */
71
+#define STP_TYPE_RSTP 0x02
72
+
73
+/** Port is forwarding */
74
+#define STP_FL_FORWARDING 0x20
75
+
76
+#endif /* _IPXE_STP_H */

+ 152
- 0
src/net/stp.c View File

@@ -0,0 +1,152 @@
1
+/*
2
+ * Copyright (C) 2015 Michael Brown <mbrown@fensystems.co.uk>.
3
+ *
4
+ * This program is free software; you can redistribute it and/or
5
+ * modify it under the terms of the GNU General Public License as
6
+ * published by the Free Software Foundation; either version 2 of the
7
+ * License, or (at your option) any later version.
8
+ *
9
+ * This program is distributed in the hope that it will be useful, but
10
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12
+ * General Public License for more details.
13
+ *
14
+ * You should have received a copy of the GNU General Public License
15
+ * along with this program; if not, write to the Free Software
16
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17
+ * 02110-1301, USA.
18
+ *
19
+ * You can also choose to distribute this program under the terms of
20
+ * the Unmodified Binary Distribution Licence (as given in the file
21
+ * COPYING.UBDL), provided that you have satisfied its requirements.
22
+ */
23
+
24
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
25
+
26
+#include <errno.h>
27
+#include <byteswap.h>
28
+#include <ipxe/netdevice.h>
29
+#include <ipxe/ethernet.h>
30
+#include <ipxe/iobuf.h>
31
+#include <ipxe/timer.h>
32
+#include <ipxe/stp.h>
33
+
34
+/** @file
35
+ *
36
+ * Spanning Tree Protocol (STP)
37
+ *
38
+ */
39
+
40
+/* Disambiguate the various error causes */
41
+#define ENOTSUP_PROTOCOL __einfo_error ( EINFO_ENOTSUP_PROTOCOL )
42
+#define EINFO_ENOTSUP_PROTOCOL					\
43
+	__einfo_uniqify ( EINFO_ENOTSUP, 0x01,			\
44
+			  "Non-STP packet received" )
45
+#define ENOTSUP_VERSION __einfo_error ( EINFO_ENOTSUP_VERSION )
46
+#define EINFO_ENOTSUP_VERSION					\
47
+	__einfo_uniqify ( EINFO_ENOTSUP, 0x01,			\
48
+			  "Legacy STP packet received" )
49
+#define ENOTSUP_TYPE __einfo_error ( EINFO_ENOTSUP_TYPE )
50
+#define EINFO_ENOTSUP_TYPE					\
51
+	__einfo_uniqify ( EINFO_ENOTSUP, 0x01,			\
52
+			  "Non-RSTP packet received" )
53
+
54
+/**
55
+ * Process incoming STP packets
56
+ *
57
+ * @v iobuf		I/O buffer
58
+ * @v netdev		Network device
59
+ * @v ll_source		Link-layer source address
60
+ * @v flags		Packet flags
61
+ * @ret rc		Return status code
62
+ */
63
+static int stp_rx ( struct io_buffer *iobuf, struct net_device *netdev,
64
+		    const void *ll_dest __unused,
65
+		    const void *ll_source __unused,
66
+		    unsigned int flags __unused ) {
67
+	struct stp_bpdu *stp;
68
+	unsigned int timeout;
69
+	int rc;
70
+
71
+	/* Sanity check */
72
+	if ( iob_len ( iobuf ) < sizeof ( *stp ) ) {
73
+		DBGC ( netdev, "STP %s received underlength packet (%zd "
74
+		       "bytes):\n", netdev->name, iob_len ( iobuf ) );
75
+		DBGC_HDA ( netdev, 0, iobuf->data, iob_len ( iobuf ) );
76
+		rc = -EINVAL;
77
+		goto done;
78
+	}
79
+	stp = iobuf->data;
80
+
81
+	/* Ignore non-RSTP packets */
82
+	if ( stp->protocol != htons ( STP_PROTOCOL ) ) {
83
+		DBGC ( netdev, "STP %s ignoring non-STP packet (protocol "
84
+		       "%#04x)\n", netdev->name, ntohs ( stp->protocol ) );
85
+		rc = -ENOTSUP_PROTOCOL;
86
+		goto done;
87
+	}
88
+	if ( stp->version < STP_VERSION_RSTP ) {
89
+		DBGC ( netdev, "STP %s received legacy STP packet (version "
90
+		       "%#02x)\n", netdev->name, stp->version );
91
+		rc = -ENOTSUP_VERSION;
92
+		goto done;
93
+	}
94
+	if ( stp->type != STP_TYPE_RSTP ) {
95
+		DBGC ( netdev, "STP %s received non-RSTP packet (type %#02x)\n",
96
+		       netdev->name, stp->type );
97
+		rc = -ENOTSUP_TYPE;
98
+		goto done;
99
+	}
100
+
101
+	/* Dump information */
102
+	DBGC2 ( netdev, "STP %s %s port %#04x flags %#02x hello %d delay %d\n",
103
+		netdev->name, eth_ntoa ( stp->sender.mac ), ntohs ( stp->port ),
104
+		stp->flags, ntohs ( stp->hello ), ntohs ( stp->delay ) );
105
+
106
+	/* Check if port is forwarding */
107
+	if ( ! ( stp->flags & STP_FL_FORWARDING ) ) {
108
+		/* Port is not forwarding: block link for two hello times */
109
+		DBGC ( netdev, "STP %s %s port %#04x flags %#02x is not "
110
+		       "forwarding\n",
111
+		       netdev->name, eth_ntoa ( stp->sender.mac ),
112
+		       ntohs ( stp->port ), stp->flags );
113
+		timeout = ( ntohs ( stp->hello ) * TICKS_PER_SEC * 2 );
114
+		netdev_link_block ( netdev, timeout );
115
+		rc = -ENETUNREACH;
116
+		goto done;
117
+	}
118
+
119
+	/* Success */
120
+	if ( netdev_link_blocked ( netdev ) ) {
121
+		DBGC ( netdev, "STP %s %s port %#04x flags %#02x is "
122
+		       "forwarding\n",
123
+		       netdev->name, eth_ntoa ( stp->sender.mac ),
124
+		       ntohs ( stp->port ), stp->flags );
125
+	}
126
+	netdev_link_unblock ( netdev );
127
+	rc = 0;
128
+
129
+ done:
130
+	free_iob ( iobuf );
131
+	return rc;
132
+}
133
+
134
+/**
135
+ * Transcribe STP address
136
+ *
137
+ * @v net_addr		STP address
138
+ * @ret string		"<STP>"
139
+ *
140
+ * This operation is meaningless for the STP protocol.
141
+ */
142
+static const char * stp_ntoa ( const void *net_addr __unused ) {
143
+	return "<STP>";
144
+}
145
+
146
+/** STP network protocol */
147
+struct net_protocol stp_protocol __net_protocol = {
148
+	.name = "STP",
149
+	.net_proto = htons ( ETH_P_STP ),
150
+	.rx = stp_rx,
151
+	.ntoa = stp_ntoa,
152
+};

Loading…
Cancel
Save