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