iPXE
eth_slow.c
Go to the documentation of this file.
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
24FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
25FILE_SECBOOT ( PERMITTED );
26
27#include <stdlib.h>
28#include <string.h>
29#include <byteswap.h>
30#include <errno.h>
31#include <ipxe/timer.h>
32#include <ipxe/iobuf.h>
33#include <ipxe/netdevice.h>
34#include <ipxe/if_ether.h>
35#include <ipxe/ethernet.h>
36#include <ipxe/eth_slow.h>
37
38/** @file
39 *
40 * Ethernet slow protocols
41 *
42 * We implement a very simple passive LACP entity, that pretends that
43 * each port is the only port on an individual system. We avoid the
44 * need for timeout logic (and retaining local state about our
45 * partner) by requesting the same timeout period (1s or 30s) as our
46 * partner requests, and then simply responding to every packet the
47 * partner sends us.
48 */
49
50struct net_protocol eth_slow_protocol __net_protocol;
51
52/** Slow protocols multicast address */
54 { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x02 };
55
56/**
57 * Name LACP TLV type
58 *
59 * @v type LACP TLV type
60 * @ret name Name of LACP TLV type
61 */
62static inline __attribute__ (( always_inline )) const char *
64 switch ( type ) {
65 case ETH_SLOW_TLV_TERMINATOR: return "terminator";
66 case ETH_SLOW_TLV_LACP_ACTOR: return "actor";
67 case ETH_SLOW_TLV_LACP_PARTNER: return "partner";
68 case ETH_SLOW_TLV_LACP_COLLECTOR: return "collector";
69 default: return "<invalid>";
70 }
71}
72
73/**
74 * Name marker TLV type
75 *
76 * @v type Marker TLV type
77 * @ret name Name of marker TLV type
78 */
79static inline __attribute__ (( always_inline )) const char *
81 switch ( type ) {
82 case ETH_SLOW_TLV_TERMINATOR: return "terminator";
83 case ETH_SLOW_TLV_MARKER_REQUEST: return "request";
84 case ETH_SLOW_TLV_MARKER_RESPONSE: return "response";
85 default: return "<invalid>";
86 }
87}
88
89/**
90 * Name LACP state
91 *
92 * @v state LACP state
93 * @ret name LACP state name
94 */
95static const char * eth_slow_lacp_state_name ( uint8_t state ) {
96 static char state_chars[] = "AFGSCDLX";
97 unsigned int i;
98
99 for ( i = 0 ; i < 8 ; i++ ) {
100 state_chars[i] |= 0x20;
101 if ( state & ( 1 << i ) )
102 state_chars[i] &= ~0x20;
103 }
104 return state_chars;
105}
106
107/**
108 * Dump LACP packet
109 *
110 * @v iobuf I/O buffer
111 * @v netdev Network device
112 * @v label "RX" or "TX"
113 */
114static void eth_slow_lacp_dump ( struct io_buffer *iobuf,
115 struct net_device *netdev,
116 const char *label ) {
117 union eth_slow_packet *eth_slow = iobuf->data;
118 struct eth_slow_lacp *lacp = &eth_slow->lacp;
119
120 DBGC ( netdev,
121 "SLOW %s %s LACP actor (%04x,%s,%04x,%02x,%04x) [%s]\n",
122 netdev->name, label, ntohs ( lacp->actor.system_priority ),
123 eth_ntoa ( lacp->actor.system ),
124 ntohs ( lacp->actor.key ),
125 ntohs ( lacp->actor.port_priority ),
126 ntohs ( lacp->actor.port ),
127 eth_slow_lacp_state_name ( lacp->actor.state ) );
128 DBGC ( netdev,
129 "SLOW %s %s LACP partner (%04x,%s,%04x,%02x,%04x) [%s]\n",
130 netdev->name, label, ntohs ( lacp->partner.system_priority ),
131 eth_ntoa ( lacp->partner.system ),
132 ntohs ( lacp->partner.key ),
133 ntohs ( lacp->partner.port_priority ),
134 ntohs ( lacp->partner.port ),
135 eth_slow_lacp_state_name ( lacp->partner.state ) );
136 DBGC ( netdev, "SLOW %s %s LACP collector %04x (%d us)\n",
137 netdev->name, label, ntohs ( lacp->collector.max_delay ),
138 ( ntohs ( lacp->collector.max_delay ) * 10 ) );
139 DBGC2_HDA ( netdev, 0, iobuf->data, iob_len ( iobuf ) );
140}
141
142/**
143 * Process incoming LACP packet
144 *
145 * @v iobuf I/O buffer
146 * @v netdev Network device
147 * @ret rc Return status code
148 */
149static int eth_slow_lacp_rx ( struct io_buffer *iobuf,
150 struct net_device *netdev ) {
151 union eth_slow_packet *eth_slow = iobuf->data;
152 struct eth_slow_lacp *lacp = &eth_slow->lacp;
153 unsigned int interval;
154
155 eth_slow_lacp_dump ( iobuf, netdev, "RX" );
156
157 /* Check for looped-back packets */
158 if ( memcmp ( lacp->actor.system, netdev->ll_addr,
159 sizeof ( lacp->actor.system ) ) == 0 ) {
160 DBGC ( netdev, "SLOW %s RX loopback detected\n",
161 netdev->name );
162 return -ELOOP;
163 }
164
165 /* If partner is not in sync, collecting, and distributing,
166 * then block the link until after the next expected LACP
167 * packet.
168 */
169 if ( ~lacp->actor.state & ( LACP_STATE_IN_SYNC |
172 DBGC ( netdev, "SLOW %s LACP partner is down\n", netdev->name );
173 interval = ( ( lacp->actor.state & LACP_STATE_FAST ) ?
174 ( ( LACP_INTERVAL_FAST + 1 ) * TICKS_PER_SEC ) :
175 ( ( LACP_INTERVAL_SLOW + 1 ) * TICKS_PER_SEC ) );
176 netdev_link_block ( netdev, interval );
177 } else {
178 if ( netdev_link_blocked ( netdev ) ) {
179 DBGC ( netdev, "SLOW %s LACP partner is up\n",
180 netdev->name );
181 }
183 }
184
185 /* Build response */
186 memset ( lacp->reserved, 0, sizeof ( lacp->reserved ) );
187 memset ( &lacp->terminator, 0, sizeof ( lacp->terminator ) );
188 memset ( &lacp->collector, 0, sizeof ( lacp->collector ) );
189 lacp->collector.tlv.type = ETH_SLOW_TLV_LACP_COLLECTOR;
190 lacp->collector.tlv.length = ETH_SLOW_TLV_LACP_COLLECTOR_LEN;
191 memcpy ( &lacp->partner, &lacp->actor, sizeof ( lacp->partner ) );
192 lacp->partner.tlv.type = ETH_SLOW_TLV_LACP_PARTNER;
193 lacp->partner.tlv.length = ETH_SLOW_TLV_LACP_PARTNER_LEN;
194 memset ( &lacp->partner.reserved, 0,
195 sizeof ( lacp->partner.reserved ) );
196 memset ( &lacp->actor, 0, sizeof ( lacp->actor ) );
197 lacp->actor.tlv.type = ETH_SLOW_TLV_LACP_ACTOR;
198 lacp->actor.tlv.length = ETH_SLOW_TLV_LACP_ACTOR_LEN;
199 lacp->actor.system_priority = htons ( LACP_SYSTEM_PRIORITY_MAX );
200 memcpy ( lacp->actor.system, netdev->ll_addr,
201 sizeof ( lacp->actor.system ) );
202 lacp->actor.key = htons ( 1 );
203 lacp->actor.port_priority = htons ( LACP_PORT_PRIORITY_MAX );
204 lacp->actor.port = htons ( 1 );
205 lacp->actor.state = ( LACP_STATE_AGGREGATABLE |
209 ( lacp->partner.state & LACP_STATE_FAST ) );
210 lacp->header.version = ETH_SLOW_LACP_VERSION;
211
212 /* Send response */
213 eth_slow_lacp_dump ( iobuf, netdev, "TX" );
214 return net_tx ( iobuf, netdev, &eth_slow_protocol, eth_slow_address,
215 netdev->ll_addr );
216}
217
218/**
219 * Dump marker packet
220 *
221 * @v iobuf I/O buffer
222 * @v netdev Network device
223 * @v label "RX" or "TX"
224 */
225static void eth_slow_marker_dump ( struct io_buffer *iobuf,
226 struct net_device *netdev,
227 const char *label ) {
228 union eth_slow_packet *eth_slow = iobuf->data;
229 struct eth_slow_marker *marker = &eth_slow->marker;
230
231 DBGC ( netdev, "SLOW %s %s marker %s port %04x system %s xact %08x\n",
232 netdev->name, label,
233 eth_slow_marker_tlv_name ( marker->marker.tlv.type ),
234 ntohs ( marker->marker.port ),
235 eth_ntoa ( marker->marker.system ),
236 ntohl ( marker->marker.xact ) );
237 DBGC2_HDA ( netdev, 0, iobuf->data, iob_len ( iobuf ) );
238}
239
240/**
241 * Process incoming marker packet
242 *
243 * @v iobuf I/O buffer
244 * @v netdev Network device
245 * @ret rc Return status code
246 */
247static int eth_slow_marker_rx ( struct io_buffer *iobuf,
248 struct net_device *netdev ) {
249 union eth_slow_packet *eth_slow = iobuf->data;
250 struct eth_slow_marker *marker = &eth_slow->marker;
251
252 eth_slow_marker_dump ( iobuf, netdev, "RX" );
253
254 if ( marker->marker.tlv.type == ETH_SLOW_TLV_MARKER_REQUEST ) {
255 /* Send marker response */
256 marker->marker.tlv.type = ETH_SLOW_TLV_MARKER_RESPONSE;
257 eth_slow_marker_dump ( iobuf, netdev, "TX" );
258 return net_tx ( iobuf, netdev, &eth_slow_protocol,
259 eth_slow_address, netdev->ll_addr );
260 } else {
261 /* Discard all other marker packets */
262 free_iob ( iobuf );
263 return -EINVAL;
264 }
265}
266
267/**
268 * Process incoming slow packet
269 *
270 * @v iobuf I/O buffer
271 * @v netdev Network device
272 * @v ll_dest Link-layer destination address
273 * @v ll_source Link-layer source address
274 * @v flags Packet flags
275 * @ret rc Return status code
276 */
277static int eth_slow_rx ( struct io_buffer *iobuf,
278 struct net_device *netdev,
279 const void *ll_dest __unused,
280 const void *ll_source __unused,
281 unsigned int flags __unused ) {
282 union eth_slow_packet *eth_slow = iobuf->data;
283
284 /* Sanity checks */
285 if ( iob_len ( iobuf ) < sizeof ( *eth_slow ) ) {
286 free_iob ( iobuf );
287 return -EINVAL;
288 }
289
290 /* Strip any trailing padding */
291 iob_unput ( iobuf, ( sizeof ( *eth_slow ) - iob_len ( iobuf ) ) );
292
293 /* Handle according to subtype */
294 switch ( eth_slow->header.subtype ) {
296 return eth_slow_lacp_rx ( iobuf, netdev );
298 return eth_slow_marker_rx ( iobuf, netdev );
299 default:
300 DBGC ( netdev, "SLOW %s RX unknown subtype %02x\n",
301 netdev->name, eth_slow->header.subtype );
302 free_iob ( iobuf );
303 return -EINVAL;
304 }
305}
306
307/** Slow protocol */
308struct net_protocol eth_slow_protocol __net_protocol = {
309 .name = "Slow",
310 .net_proto = htons ( ETH_P_SLOW ),
311 .rx = eth_slow_rx,
312};
unsigned char uint8_t
Definition stdint.h:10
uint32_t type
Operating system type.
Definition ena.h:1
uint8_t flags
Flags.
Definition ena.h:7
Error codes.
static int eth_slow_lacp_rx(struct io_buffer *iobuf, struct net_device *netdev)
Process incoming LACP packet.
Definition eth_slow.c:149
static int 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)
Process incoming slow packet.
Definition eth_slow.c:277
static const uint8_t eth_slow_address[ETH_ALEN]
Slow protocols multicast address.
Definition eth_slow.c:53
static const char * eth_slow_marker_tlv_name(uint8_t type)
Name marker TLV type.
Definition eth_slow.c:80
static void eth_slow_lacp_dump(struct io_buffer *iobuf, struct net_device *netdev, const char *label)
Dump LACP packet.
Definition eth_slow.c:114
static int eth_slow_marker_rx(struct io_buffer *iobuf, struct net_device *netdev)
Process incoming marker packet.
Definition eth_slow.c:247
static const char * eth_slow_lacp_tlv_name(uint8_t type)
Name LACP TLV type.
Definition eth_slow.c:63
static const char * eth_slow_lacp_state_name(uint8_t state)
Name LACP state.
Definition eth_slow.c:95
static void eth_slow_marker_dump(struct io_buffer *iobuf, struct net_device *netdev, const char *label)
Dump marker packet.
Definition eth_slow.c:225
Ethernet slow protocols.
#define LACP_STATE_COLLECTING
LACP link is collecting (receiving)
Definition eth_slow.h:174
uint8_t state
State.
Definition eth_slow.h:36
#define ETH_SLOW_TLV_LACP_COLLECTOR
LACP collector type.
Definition eth_slow.h:69
#define LACP_STATE_AGGREGATABLE
LACP link is aggregateable.
Definition eth_slow.h:162
#define ETH_SLOW_TLV_LACP_COLLECTOR_LEN
LACP collector length.
Definition eth_slow.h:72
#define ETH_SLOW_TLV_LACP_ACTOR_LEN
LACP actor length.
Definition eth_slow.h:58
#define LACP_INTERVAL_FAST
LACP fast interval (1 second)
Definition eth_slow.h:195
#define ETH_SLOW_SUBTYPE_LACP
LACP subtype.
Definition eth_slow.h:22
#define LACP_SYSTEM_PRIORITY_MAX
Maximum system priority.
Definition eth_slow.h:139
#define ETH_SLOW_LACP_VERSION
LACP version number.
Definition eth_slow.h:25
#define LACP_STATE_FAST
LACP timeout is short.
Definition eth_slow.h:156
#define LACP_INTERVAL_SLOW
LACP slow interval (30 seconds)
Definition eth_slow.h:198
#define LACP_STATE_IN_SYNC
LACP link is in synchronisation.
Definition eth_slow.h:168
#define ETH_SLOW_TLV_LACP_ACTOR
LACP actor type.
Definition eth_slow.h:55
#define ETH_SLOW_TLV_LACP_PARTNER
LACP partner type.
Definition eth_slow.h:62
#define ETH_SLOW_SUBTYPE_MARKER
Marker subtype.
Definition eth_slow.h:28
#define ETH_SLOW_TLV_TERMINATOR
Terminator type.
Definition eth_slow.h:49
#define ETH_SLOW_TLV_LACP_PARTNER_LEN
LACP partner length.
Definition eth_slow.h:65
#define ETH_SLOW_TLV_MARKER_RESPONSE
Marker response type.
Definition eth_slow.h:83
#define LACP_STATE_DISTRIBUTING
LACP link is distributing (transmitting)
Definition eth_slow.h:180
#define ETH_SLOW_TLV_MARKER_REQUEST
Marker request type.
Definition eth_slow.h:76
#define LACP_PORT_PRIORITY_MAX
Maximum port priority.
Definition eth_slow.h:142
struct eth_slow_marker_tlv marker
Marker information.
Definition eth_slow.h:3
struct eth_slow_lacp lacp
LACP packet.
Definition eth_slow.h:3
const char * eth_ntoa(const void *ll_addr)
Transcribe Ethernet address.
Definition ethernet.c:176
Ethernet protocol.
static struct net_device * netdev
Definition gdbudp.c:53
#define __unused
Declare a variable or data structure as unused.
Definition compiler.h:573
#define DBGC2_HDA(...)
Definition compiler.h:523
#define DBGC(...)
Definition compiler.h:505
#define FILE_LICENCE(_licence)
Declare a particular licence as applying to a file.
Definition compiler.h:896
#define EINVAL
Invalid argument.
Definition errno.h:429
#define ELOOP
Too many levels of symbolic links.
Definition errno.h:449
#define FILE_SECBOOT(_status)
Declare a file's UEFI Secure Boot permission status.
Definition compiler.h:926
#define ETH_P_SLOW
Definition if_ether.h:24
#define ETH_ALEN
Definition if_ether.h:9
#define ntohl(value)
Definition byteswap.h:135
#define htons(value)
Definition byteswap.h:136
#define ntohs(value)
Definition byteswap.h:137
#define __attribute__(x)
Definition compiler.h:10
iPXE timers
#define TICKS_PER_SEC
Number of ticks per second.
Definition timer.h:16
String functions.
void * memcpy(void *dest, const void *src, size_t len) __nonnull
void * memset(void *dest, int character, size_t len) __nonnull
void free_iob(struct io_buffer *iobuf)
Free I/O buffer.
Definition iobuf.c:153
I/O buffers.
static size_t iob_len(struct io_buffer *iobuf)
Calculate length of data in an I/O buffer.
Definition iobuf.h:160
#define iob_unput(iobuf, len)
Definition iobuf.h:140
int net_tx(struct io_buffer *iobuf, struct net_device *netdev, struct net_protocol *net_protocol, const void *ll_dest, const void *ll_source)
Transmit network-layer packet.
Definition netdevice.c:1074
void netdev_link_block(struct net_device *netdev, unsigned long timeout)
Mark network device link as being blocked.
Definition netdevice.c:248
void netdev_link_unblock(struct net_device *netdev)
Mark network device link as being unblocked.
Definition netdevice.c:263
Network device management.
static int netdev_link_blocked(struct net_device *netdev)
Check link block state of network device.
Definition netdevice.h:651
#define __net_protocol
Declare a network-layer protocol.
Definition netdevice.h:474
int memcmp(const void *first, const void *second, size_t len)
Compare memory regions.
Definition string.c:115
uint8_t subtype
Slow protocols subtype.
Definition eth_slow.h:16
LACP packet.
Definition eth_slow.h:225
Marker packet.
Definition eth_slow.h:241
A persistent I/O buffer.
Definition iobuf.h:38
void * data
Start of data.
Definition iobuf.h:53
A text label widget.
Definition label.h:16
A network device.
Definition netdevice.h:353
A network-layer protocol.
Definition netdevice.h:65
Slow protocols packet.
Definition eth_slow.h:253
struct eth_slow_header header
Slow protocols header.
Definition eth_slow.h:255
struct eth_slow_lacp lacp
LACP packet.
Definition eth_slow.h:257
struct eth_slow_marker marker
Marker packet.
Definition eth_slow.h:259