[zbin] Fix check for existence of most recent output byte
[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
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 };