2 * Copyright (C) 2010 Michael Brown <mbrown@fensystems.co.uk>.
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.
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.
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.
19 FILE_LICENCE ( GPL2_OR_LATER
);
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>
33 * Ethernet slow protocols
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
43 struct net_protocol eth_slow_protocol
;
45 /** Slow protocols multicast address */
46 static const uint8_t eth_slow_address
[ETH_ALEN
] =
47 { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x02 };
52 * @v type LACP TLV type
53 * @ret name Name of LACP TLV type
55 static inline __attribute__ (( always_inline
)) const char *
56 eth_slow_lacp_tlv_name ( uint8_t 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>";
67 * Name marker TLV type
69 * @v type Marker TLV type
70 * @ret name Name of marker TLV type
72 static inline __attribute__ (( always_inline
)) const char *
73 eth_slow_marker_tlv_name ( uint8_t 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>";
86 * @ret name LACP state name
88 static const char * eth_slow_lacp_state_name ( uint8_t state
) {
89 static char state_chars
[] = "AFGSRTLX";
92 for ( i
= 0 ; i
< 8 ; i
++ ) {
93 state_chars
[i
] |= 0x20;
94 if ( state
& ( 1 << i
) )
95 state_chars
[i
] &= ~0x20;
103 * @v iobuf I/O buffer
104 * @v netdev Network device
105 * @v label "RX" or "TX"
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
= ð_slow
->lacp
;
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
) );
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
) );
136 * Process incoming LACP packet
138 * @v iobuf I/O buffer
139 * @v netdev Network device
140 * @ret rc Return status code
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
= ð_slow
->lacp
;
147 eth_slow_lacp_dump ( iobuf
, netdev
, "RX" );
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
;
176 eth_slow_lacp_dump ( iobuf
, netdev
, "TX" );
177 return net_tx ( iobuf
, netdev
, ð_slow_protocol
, eth_slow_address
);
183 * @v iobuf I/O buffer
184 * @v netdev Network device
185 * @v label "RX" or "TX"
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
= ð_slow
->marker
;
193 DBGC ( netdev
, "SLOW %p %s marker %s port %04x system %s xact %08x\n",
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
) );
203 * Process incoming marker packet
205 * @v iobuf I/O buffer
206 * @v netdev Network device
207 * @ret rc Return status code
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
= ð_slow
->marker
;
214 eth_slow_marker_dump ( iobuf
, netdev
, "RX" );
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
, ð_slow_protocol
,
223 /* Discard all other marker packets */
230 * Process incoming slow packet
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
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
;
243 if ( iob_len ( iobuf
) < sizeof ( *eth_slow
) ) {
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
);
255 DBGC ( netdev
, "SLOW %p RX unknown subtype %02x\n",
256 netdev
, eth_slow
->header
.subtype
);
263 struct net_protocol eth_slow_protocol __net_protocol
= {
265 .net_proto
= htons ( ETH_P_SLOW
),