iPXE - Open Source Boot Firmware

[lacp] Check the partner's own state when checking for blocked links
[ipxe.git] / src / net / eth_slow.c
1 /*
2  * Copyright (C) 2010 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 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 <stdlib.h>
27 #include <string.h>
28 #include <byteswap.h>
29 #include <errno.h>
30 #include <ipxe/timer.h>
31 #include <ipxe/iobuf.h>
32 #include <ipxe/netdevice.h>
33 #include <ipxe/if_ether.h>
34 #include <ipxe/ethernet.h>
35 #include <ipxe/eth_slow.h>
36
37 /** @file
38  *
39  * Ethernet slow protocols
40  *
41  * We implement a very simple passive LACP entity, that pretends that
42  * each port is the only port on an individual system.  We avoid the
43  * need for timeout logic (and retaining local state about our
44  * partner) by requesting the same timeout period (1s or 30s) as our
45  * partner requests, and then simply responding to every packet the
46  * partner sends us.
47  */
48
49 struct net_protocol eth_slow_protocol __net_protocol;
50
51 /** Slow protocols multicast address */
52 static const uint8_t eth_slow_address[ETH_ALEN] =
53         { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x02 };
54
55 /**
56  * Name LACP TLV type
57  *
58  * @v type              LACP TLV type
59  * @ret name            Name of LACP TLV type
60  */
61 static inline __attribute__ (( always_inline )) const char *
62 eth_slow_lacp_tlv_name ( uint8_t type ) {
63         switch ( type ) {
64         case ETH_SLOW_TLV_TERMINATOR:           return "terminator";
65         case ETH_SLOW_TLV_LACP_ACTOR:           return "actor";
66         case ETH_SLOW_TLV_LACP_PARTNER:         return "partner";
67         case ETH_SLOW_TLV_LACP_COLLECTOR:       return "collector";
68         default:                                return "<invalid>";
69         }
70 }
71
72 /**
73  * Name marker TLV type
74  *
75  * @v type              Marker TLV type
76  * @ret name            Name of marker TLV type
77  */
78 static inline __attribute__ (( always_inline )) const char *
79 eth_slow_marker_tlv_name ( uint8_t type ) {
80         switch ( type ) {
81         case ETH_SLOW_TLV_TERMINATOR:           return "terminator";
82         case ETH_SLOW_TLV_MARKER_REQUEST:       return "request";
83         case ETH_SLOW_TLV_MARKER_RESPONSE:      return "response";
84         default:                                return "<invalid>";
85         }
86 }
87
88 /**
89  * Name LACP state
90  *
91  * @v state             LACP state
92  * @ret name            LACP state name
93  */
94 static const char * eth_slow_lacp_state_name ( uint8_t state ) {
95         static char state_chars[] = "AFGSCDLX";
96         unsigned int i;
97
98         for ( i = 0 ; i < 8 ; i++ ) {
99                 state_chars[i] |= 0x20;
100                 if ( state & ( 1 << i ) )
101                         state_chars[i] &= ~0x20;
102         }
103         return state_chars;
104 }
105
106 /**
107  * Dump LACP packet
108  *
109  * @v iobuf             I/O buffer
110  * @v netdev            Network device
111  * @v label             "RX" or "TX"
112  */
113 static void eth_slow_lacp_dump ( struct io_buffer *iobuf,
114                                  struct net_device *netdev,
115                                  const char *label ) {
116         union eth_slow_packet *eth_slow = iobuf->data;
117         struct eth_slow_lacp *lacp = &eth_slow->lacp;
118
119         DBGC ( netdev,
120                "SLOW %s %s LACP actor (%04x,%s,%04x,%02x,%04x) [%s]\n",
121                netdev->name, label, ntohs ( lacp->actor.system_priority ),
122                eth_ntoa ( lacp->actor.system ),
123                ntohs ( lacp->actor.key ),
124                ntohs ( lacp->actor.port_priority ),
125                ntohs ( lacp->actor.port ),
126                eth_slow_lacp_state_name ( lacp->actor.state ) );
127         DBGC ( netdev,
128                "SLOW %s %s LACP partner (%04x,%s,%04x,%02x,%04x) [%s]\n",
129                netdev->name, label, ntohs ( lacp->partner.system_priority ),
130                eth_ntoa ( lacp->partner.system ),
131                ntohs ( lacp->partner.key ),
132                ntohs ( lacp->partner.port_priority ),
133                ntohs ( lacp->partner.port ),
134                eth_slow_lacp_state_name ( lacp->partner.state ) );
135         DBGC ( netdev, "SLOW %s %s LACP collector %04x (%d us)\n",
136                netdev->name, label, ntohs ( lacp->collector.max_delay ),
137                ( ntohs ( lacp->collector.max_delay ) * 10 ) );
138         DBGC2_HDA ( netdev, 0, iobuf->data, iob_len ( iobuf ) );
139 }
140
141 /**
142  * Process incoming LACP packet
143  *
144  * @v iobuf             I/O buffer
145  * @v netdev            Network device
146  * @ret rc              Return status code
147  */
148 static int eth_slow_lacp_rx ( struct io_buffer *iobuf,
149                               struct net_device *netdev ) {
150         union eth_slow_packet *eth_slow = iobuf->data;
151         struct eth_slow_lacp *lacp = &eth_slow->lacp;
152         unsigned int interval;
153
154         eth_slow_lacp_dump ( iobuf, netdev, "RX" );
155
156         /* If partner is not in sync, collecting, and distributing,
157          * then block the link until after the next expected LACP
158          * packet.
159          */
160         if ( ~lacp->actor.state & ( LACP_STATE_IN_SYNC |
161                                     LACP_STATE_COLLECTING |
162                                     LACP_STATE_DISTRIBUTING ) ) {
163                 DBGC ( netdev, "SLOW %s LACP partner is down\n", netdev->name );
164                 interval = ( ( lacp->actor.state & LACP_STATE_FAST ) ?
165                              ( ( LACP_INTERVAL_FAST + 1 ) * TICKS_PER_SEC ) :
166                              ( ( LACP_INTERVAL_SLOW + 1 ) * TICKS_PER_SEC ) );
167                 netdev_link_block ( netdev, interval );
168         } else {
169                 if ( netdev_link_blocked ( netdev ) ) {
170                         DBGC ( netdev, "SLOW %s LACP partner is up\n",
171                                netdev->name );
172                 }
173                 netdev_link_unblock ( netdev );
174         }
175
176         /* Build response */
177         memset ( lacp->reserved, 0, sizeof ( lacp->reserved ) );
178         memset ( &lacp->terminator, 0, sizeof ( lacp->terminator ) );
179         memset ( &lacp->collector, 0, sizeof ( lacp->collector ) );
180         lacp->collector.tlv.type = ETH_SLOW_TLV_LACP_COLLECTOR;
181         lacp->collector.tlv.length = ETH_SLOW_TLV_LACP_COLLECTOR_LEN;
182         memcpy ( &lacp->partner, &lacp->actor, sizeof ( lacp->partner ) );
183         lacp->partner.tlv.type = ETH_SLOW_TLV_LACP_PARTNER;
184         lacp->partner.tlv.length = ETH_SLOW_TLV_LACP_PARTNER_LEN;
185         memset ( &lacp->partner.reserved, 0,
186                  sizeof ( lacp->partner.reserved ) );
187         memset ( &lacp->actor, 0, sizeof ( lacp->actor ) );
188         lacp->actor.tlv.type = ETH_SLOW_TLV_LACP_ACTOR;
189         lacp->actor.tlv.length = ETH_SLOW_TLV_LACP_ACTOR_LEN;
190         lacp->actor.system_priority = htons ( LACP_SYSTEM_PRIORITY_MAX );
191         memcpy ( lacp->actor.system, netdev->ll_addr,
192                  sizeof ( lacp->actor.system ) );
193         lacp->actor.key = htons ( 1 );
194         lacp->actor.port_priority = htons ( LACP_PORT_PRIORITY_MAX );
195         lacp->actor.port = htons ( 1 );
196         lacp->actor.state = ( LACP_STATE_AGGREGATABLE |
197                               LACP_STATE_IN_SYNC |
198                               LACP_STATE_COLLECTING |
199                               LACP_STATE_DISTRIBUTING |
200                               ( lacp->partner.state & LACP_STATE_FAST ) );
201         lacp->header.version = ETH_SLOW_LACP_VERSION;
202
203         /* Send response */
204         eth_slow_lacp_dump ( iobuf, netdev, "TX" );
205         return net_tx ( iobuf, netdev, &eth_slow_protocol, eth_slow_address,
206                         netdev->ll_addr );
207 }
208
209 /**
210  * Dump marker packet
211  *
212  * @v iobuf             I/O buffer
213  * @v netdev            Network device
214  * @v label             "RX" or "TX"
215  */
216 static void eth_slow_marker_dump ( struct io_buffer *iobuf,
217                                    struct net_device *netdev,
218                                    const char *label ) {
219         union eth_slow_packet *eth_slow = iobuf->data;
220         struct eth_slow_marker *marker = &eth_slow->marker;
221
222         DBGC ( netdev, "SLOW %s %s marker %s port %04x system %s xact %08x\n",
223                netdev->name, label,
224                eth_slow_marker_tlv_name ( marker->marker.tlv.type ),
225                ntohs ( marker->marker.port ),
226                eth_ntoa ( marker->marker.system ),
227                ntohl ( marker->marker.xact ) );
228         DBGC2_HDA ( netdev, 0, iobuf->data, iob_len ( iobuf ) );
229 }
230
231 /**
232  * Process incoming marker packet
233  *
234  * @v iobuf             I/O buffer
235  * @v netdev            Network device
236  * @ret rc              Return status code
237  */
238 static int eth_slow_marker_rx ( struct io_buffer *iobuf,
239                                 struct net_device *netdev ) {
240         union eth_slow_packet *eth_slow = iobuf->data;
241         struct eth_slow_marker *marker = &eth_slow->marker;
242
243         eth_slow_marker_dump ( iobuf, netdev, "RX" );
244
245         if ( marker->marker.tlv.type == ETH_SLOW_TLV_MARKER_REQUEST ) {
246                 /* Send marker response */
247                 marker->marker.tlv.type = ETH_SLOW_TLV_MARKER_RESPONSE;
248                 eth_slow_marker_dump ( iobuf, netdev, "TX" );
249                 return net_tx ( iobuf, netdev, &eth_slow_protocol,
250                                 eth_slow_address, netdev->ll_addr );
251         } else {
252                 /* Discard all other marker packets */
253                 free_iob ( iobuf );
254                 return -EINVAL;
255         }
256 }
257
258 /**
259  * Process incoming slow packet
260  *
261  * @v iobuf             I/O buffer
262  * @v netdev            Network device
263  * @v ll_dest           Link-layer destination address
264  * @v ll_source         Link-layer source address
265  * @v flags             Packet flags
266  * @ret rc              Return status code
267  */
268 static int eth_slow_rx ( struct io_buffer *iobuf,
269                          struct net_device *netdev,
270                          const void *ll_dest __unused,
271                          const void *ll_source __unused,
272                          unsigned int flags __unused ) {
273         union eth_slow_packet *eth_slow = iobuf->data;
274
275         /* Sanity checks */
276         if ( iob_len ( iobuf ) < sizeof ( *eth_slow ) ) {
277                 free_iob ( iobuf );
278                 return -EINVAL;
279         }
280
281         /* Handle according to subtype */
282         switch ( eth_slow->header.subtype ) {
283         case ETH_SLOW_SUBTYPE_LACP:
284                 return eth_slow_lacp_rx ( iobuf, netdev );
285         case ETH_SLOW_SUBTYPE_MARKER:
286                 return eth_slow_marker_rx ( iobuf, netdev );
287         default:
288                 DBGC ( netdev, "SLOW %s RX unknown subtype %02x\n",
289                        netdev->name, eth_slow->header.subtype );
290                 free_iob ( iobuf );
291                 return -EINVAL;
292         }
293 }
294
295 /** Slow protocol */
296 struct net_protocol eth_slow_protocol __net_protocol = {
297         .name = "Slow",
298         .net_proto = htons ( ETH_P_SLOW ),
299         .rx = eth_slow_rx,
300 };