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 *
eth_slow_lacp_tlv_name(uint8_t type)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 *
eth_slow_marker_tlv_name(uint8_t type)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 */
eth_slow_lacp_state_name(uint8_t state)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 */
eth_slow_lacp_dump(struct io_buffer * iobuf,struct net_device * netdev,const char * label)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 = ð_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 */
eth_slow_lacp_rx(struct io_buffer * iobuf,struct net_device * netdev)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 = ð_slow->lacp;
152 unsigned int interval;
153
154 eth_slow_lacp_dump ( iobuf, netdev, "RX" );
155
156 /* Check for looped-back packets */
157 if ( memcmp ( lacp->actor.system, netdev->ll_addr,
158 sizeof ( lacp->actor.system ) ) == 0 ) {
159 DBGC ( netdev, "SLOW %s RX loopback detected\n",
160 netdev->name );
161 return -ELOOP;
162 }
163
164 /* If partner is not in sync, collecting, and distributing,
165 * then block the link until after the next expected LACP
166 * packet.
167 */
168 if ( ~lacp->actor.state & ( LACP_STATE_IN_SYNC |
169 LACP_STATE_COLLECTING |
170 LACP_STATE_DISTRIBUTING ) ) {
171 DBGC ( netdev, "SLOW %s LACP partner is down\n", netdev->name );
172 interval = ( ( lacp->actor.state & LACP_STATE_FAST ) ?
173 ( ( LACP_INTERVAL_FAST + 1 ) * TICKS_PER_SEC ) :
174 ( ( LACP_INTERVAL_SLOW + 1 ) * TICKS_PER_SEC ) );
175 netdev_link_block ( netdev, interval );
176 } else {
177 if ( netdev_link_blocked ( netdev ) ) {
178 DBGC ( netdev, "SLOW %s LACP partner is up\n",
179 netdev->name );
180 }
181 netdev_link_unblock ( netdev );
182 }
183
184 /* Build response */
185 memset ( lacp->reserved, 0, sizeof ( lacp->reserved ) );
186 memset ( &lacp->terminator, 0, sizeof ( lacp->terminator ) );
187 memset ( &lacp->collector, 0, sizeof ( lacp->collector ) );
188 lacp->collector.tlv.type = ETH_SLOW_TLV_LACP_COLLECTOR;
189 lacp->collector.tlv.length = ETH_SLOW_TLV_LACP_COLLECTOR_LEN;
190 memcpy ( &lacp->partner, &lacp->actor, sizeof ( lacp->partner ) );
191 lacp->partner.tlv.type = ETH_SLOW_TLV_LACP_PARTNER;
192 lacp->partner.tlv.length = ETH_SLOW_TLV_LACP_PARTNER_LEN;
193 memset ( &lacp->partner.reserved, 0,
194 sizeof ( lacp->partner.reserved ) );
195 memset ( &lacp->actor, 0, sizeof ( lacp->actor ) );
196 lacp->actor.tlv.type = ETH_SLOW_TLV_LACP_ACTOR;
197 lacp->actor.tlv.length = ETH_SLOW_TLV_LACP_ACTOR_LEN;
198 lacp->actor.system_priority = htons ( LACP_SYSTEM_PRIORITY_MAX );
199 memcpy ( lacp->actor.system, netdev->ll_addr,
200 sizeof ( lacp->actor.system ) );
201 lacp->actor.key = htons ( 1 );
202 lacp->actor.port_priority = htons ( LACP_PORT_PRIORITY_MAX );
203 lacp->actor.port = htons ( 1 );
204 lacp->actor.state = ( LACP_STATE_AGGREGATABLE |
205 LACP_STATE_IN_SYNC |
206 LACP_STATE_COLLECTING |
207 LACP_STATE_DISTRIBUTING |
208 ( lacp->partner.state & LACP_STATE_FAST ) );
209 lacp->header.version = ETH_SLOW_LACP_VERSION;
210
211 /* Send response */
212 eth_slow_lacp_dump ( iobuf, netdev, "TX" );
213 return net_tx ( iobuf, netdev, ð_slow_protocol, eth_slow_address,
214 netdev->ll_addr );
215 }
216
217 /**
218 * Dump marker packet
219 *
220 * @v iobuf I/O buffer
221 * @v netdev Network device
222 * @v label "RX" or "TX"
223 */
eth_slow_marker_dump(struct io_buffer * iobuf,struct net_device * netdev,const char * label)224 static void eth_slow_marker_dump ( struct io_buffer *iobuf,
225 struct net_device *netdev,
226 const char *label ) {
227 union eth_slow_packet *eth_slow = iobuf->data;
228 struct eth_slow_marker *marker = ð_slow->marker;
229
230 DBGC ( netdev, "SLOW %s %s marker %s port %04x system %s xact %08x\n",
231 netdev->name, label,
232 eth_slow_marker_tlv_name ( marker->marker.tlv.type ),
233 ntohs ( marker->marker.port ),
234 eth_ntoa ( marker->marker.system ),
235 ntohl ( marker->marker.xact ) );
236 DBGC2_HDA ( netdev, 0, iobuf->data, iob_len ( iobuf ) );
237 }
238
239 /**
240 * Process incoming marker packet
241 *
242 * @v iobuf I/O buffer
243 * @v netdev Network device
244 * @ret rc Return status code
245 */
eth_slow_marker_rx(struct io_buffer * iobuf,struct net_device * netdev)246 static int eth_slow_marker_rx ( struct io_buffer *iobuf,
247 struct net_device *netdev ) {
248 union eth_slow_packet *eth_slow = iobuf->data;
249 struct eth_slow_marker *marker = ð_slow->marker;
250
251 eth_slow_marker_dump ( iobuf, netdev, "RX" );
252
253 if ( marker->marker.tlv.type == ETH_SLOW_TLV_MARKER_REQUEST ) {
254 /* Send marker response */
255 marker->marker.tlv.type = ETH_SLOW_TLV_MARKER_RESPONSE;
256 eth_slow_marker_dump ( iobuf, netdev, "TX" );
257 return net_tx ( iobuf, netdev, ð_slow_protocol,
258 eth_slow_address, netdev->ll_addr );
259 } else {
260 /* Discard all other marker packets */
261 free_iob ( iobuf );
262 return -EINVAL;
263 }
264 }
265
266 /**
267 * Process incoming slow packet
268 *
269 * @v iobuf I/O buffer
270 * @v netdev Network device
271 * @v ll_dest Link-layer destination address
272 * @v ll_source Link-layer source address
273 * @v flags Packet flags
274 * @ret rc Return status code
275 */
eth_slow_rx(struct io_buffer * iobuf,struct net_device * netdev,const void * ll_dest __unused,const void * ll_source __unused,unsigned int flags __unused)276 static int eth_slow_rx ( struct io_buffer *iobuf,
277 struct net_device *netdev,
278 const void *ll_dest __unused,
279 const void *ll_source __unused,
280 unsigned int flags __unused ) {
281 union eth_slow_packet *eth_slow = iobuf->data;
282
283 /* Sanity checks */
284 if ( iob_len ( iobuf ) < sizeof ( *eth_slow ) ) {
285 free_iob ( iobuf );
286 return -EINVAL;
287 }
288
289 /* Strip any trailing padding */
290 iob_unput ( iobuf, ( sizeof ( *eth_slow ) - iob_len ( iobuf ) ) );
291
292 /* Handle according to subtype */
293 switch ( eth_slow->header.subtype ) {
294 case ETH_SLOW_SUBTYPE_LACP:
295 return eth_slow_lacp_rx ( iobuf, netdev );
296 case ETH_SLOW_SUBTYPE_MARKER:
297 return eth_slow_marker_rx ( iobuf, netdev );
298 default:
299 DBGC ( netdev, "SLOW %s RX unknown subtype %02x\n",
300 netdev->name, eth_slow->header.subtype );
301 free_iob ( iobuf );
302 return -EINVAL;
303 }
304 }
305
306 /** Slow protocol */
307 struct net_protocol eth_slow_protocol __net_protocol = {
308 .name = "Slow",
309 .net_proto = htons ( ETH_P_SLOW ),
310 .rx = eth_slow_rx,
311 };
312