iPXE - Open Source Boot Firmware

[peerdist] Allow PeerDist to be globally enabled or disabled
[ipxe.git] / src / net / stp.c
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, 0x02,                  \
44                           "Non-STP packet received" )
45 #define ENOTSUP_VERSION __einfo_error ( EINFO_ENOTSUP_VERSION )
46 #define EINFO_ENOTSUP_VERSION                                   \
47         __einfo_uniqify ( EINFO_ENOTSUP, 0x03,                  \
48                           "Legacy STP packet received" )
49 #define ENOTSUP_TYPE __einfo_error ( EINFO_ENOTSUP_TYPE )
50 #define EINFO_ENOTSUP_TYPE                                      \
51         __einfo_uniqify ( EINFO_ENOTSUP, 0x04,                  \
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 hello;
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                 hello = ( ntohs ( stp->hello ) * ( TICKS_PER_SEC / 256 ) );
114                 netdev_link_block ( netdev, ( hello * 2 ) );
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 };