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