iPXE
eapol.c
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2021 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 <string.h>
27 #include <assert.h>
28 #include <errno.h>
29 #include <byteswap.h>
30 #include <ipxe/iobuf.h>
31 #include <ipxe/if_ether.h>
32 #include <ipxe/if_arp.h>
33 #include <ipxe/netdevice.h>
34 #include <ipxe/vlan.h>
35 #include <ipxe/retry.h>
36 #include <ipxe/eap.h>
37 #include <ipxe/eapol.h>
38 
39 /** @file
40  *
41  * Extensible Authentication Protocol over LAN (EAPoL)
42  *
43  */
44 
45 struct net_driver eapol_driver __net_driver;
46 
47 /** EAPoL destination MAC address */
48 static const uint8_t eapol_mac[ETH_ALEN] = {
49  0x01, 0x80, 0xc2, 0x00, 0x00, 0x03
50 };
51 
52 /**
53  * Process EAPoL packet
54  *
55  * @v iobuf I/O buffer
56  * @v netdev Network device
57  * @v ll_dest Link-layer destination address
58  * @v ll_source Link-layer source address
59  * @v flags Packet flags
60  * @ret rc Return status code
61  */
62 static int eapol_rx ( struct io_buffer *iobuf, struct net_device *netdev,
63  const void *ll_dest __unused, const void *ll_source,
64  unsigned int flags __unused ) {
65  struct eapol_supplicant *supplicant;
66  struct eapol_header *eapol;
67  struct eapol_handler *handler;
68  size_t remaining;
69  size_t len;
70  int rc;
71 
72  /* Find matching supplicant */
73  supplicant = netdev_priv ( netdev, &eapol_driver );
74 
75  /* Ignore non-EAPoL devices */
76  if ( ! supplicant->eap.netdev ) {
77  DBGC ( netdev, "EAPOL %s is not an EAPoL device\n",
78  netdev->name );
79  DBGC_HDA ( netdev, 0, iobuf->data, iob_len ( iobuf ) );
80  rc = -ENOTTY;
81  goto drop;
82  }
83 
84  /* Sanity checks */
85  if ( iob_len ( iobuf ) < sizeof ( *eapol ) ) {
86  DBGC ( netdev, "EAPOL %s underlength header:\n",
87  netdev->name );
88  DBGC_HDA ( netdev, 0, iobuf->data, iob_len ( iobuf ) );
89  rc = -EINVAL;
90  goto drop;
91  }
92  eapol = iobuf->data;
93  remaining = ( iob_len ( iobuf ) - sizeof ( *eapol ) );
94  len = ntohs ( eapol->len );
95  if ( len > remaining ) {
96  DBGC ( netdev, "EAPOL %s v%d type %d len %zd underlength "
97  "payload:\n", netdev->name, eapol->version,
98  eapol->type, len );
99  DBGC_HDA ( netdev, 0, iobuf->data, iob_len ( iobuf ) );
100  rc = -EINVAL;
101  goto drop;
102  }
103 
104  /* Strip any trailing padding */
105  iob_unput ( iobuf, ( len - remaining ) );
106 
107  /* Handle according to type */
108  for_each_table_entry ( handler, EAPOL_HANDLERS ) {
109  if ( handler->type == eapol->type ) {
110  return handler->rx ( supplicant, iob_disown ( iobuf ),
111  ll_source );
112  }
113  }
114  rc = -ENOTSUP;
115  DBGC ( netdev, "EAPOL %s v%d type %d unsupported\n",
116  netdev->name, eapol->version, eapol->type );
117  DBGC_HDA ( netdev, 0, iobuf->data, iob_len ( iobuf ) );
118 
119  drop:
120  free_iob ( iobuf );
121  return rc;
122 }
123 
124 /** EAPoL protocol */
125 struct net_protocol eapol_protocol __net_protocol = {
126  .name = "EAPOL",
127  .net_proto = htons ( ETH_P_EAPOL ),
128  .rx = eapol_rx,
129 };
130 
131 /**
132  * Process EAPoL-encapsulated EAP packet
133  *
134  * @v supplicant EAPoL supplicant
135  * @v ll_source Link-layer source address
136  * @ret rc Return status code
137  */
138 static int eapol_eap_rx ( struct eapol_supplicant *supplicant,
139  struct io_buffer *iobuf,
140  const void *ll_source __unused ) {
141  struct net_device *netdev = supplicant->eap.netdev;
142  struct eapol_header *eapol;
143  int rc;
144 
145  /* Sanity check */
146  assert ( iob_len ( iobuf ) >= sizeof ( *eapol ) );
147 
148  /* Strip EAPoL header */
149  eapol = iob_pull ( iobuf, sizeof ( *eapol ) );
150 
151  /* Process EAP packet */
152  if ( ( rc = eap_rx ( &supplicant->eap, iobuf->data,
153  iob_len ( iobuf ) ) ) != 0 ) {
154  DBGC ( netdev, "EAPOL %s v%d EAP failed: %s\n",
155  netdev->name, eapol->version, strerror ( rc ) );
156  goto drop;
157  }
158 
159  /* Update EAPoL-Start transmission timer */
160  if ( supplicant->eap.flags & EAP_FL_PASSIVE ) {
161  /* Stop sending EAPoL-Start */
162  if ( timer_running ( &supplicant->timer ) ) {
163  DBGC ( netdev, "EAPOL %s becoming passive\n",
164  netdev->name );
165  }
166  stop_timer ( &supplicant->timer );
167  } else if ( supplicant->eap.flags & EAP_FL_ONGOING ) {
168  /* Delay EAPoL-Start until after next expected packet */
169  DBGC ( netdev, "EAPOL %s deferring Start\n", netdev->name );
170  start_timer_fixed ( &supplicant->timer, EAP_WAIT_TIMEOUT );
171  supplicant->count = 0;
172  }
173 
174  drop:
175  free_iob ( iobuf );
176  return rc;
177 }
178 
179 /** EAPoL handler for EAP packets */
180 struct eapol_handler eapol_eap __eapol_handler = {
181  .type = EAPOL_TYPE_EAP,
182  .rx = eapol_eap_rx,
183 };
184 
185 /**
186  * Transmit EAPoL packet
187  *
188  * @v supplicant EAPoL supplicant
189  * @v type Packet type
190  * @v data Packet body
191  * @v len Length of packet body
192  * @ret rc Return status code
193  */
194 static int eapol_tx ( struct eapol_supplicant *supplicant, unsigned int type,
195  const void *data, size_t len ) {
196  struct net_device *netdev = supplicant->eap.netdev;
197  struct io_buffer *iobuf;
198  struct eapol_header *eapol;
199  int rc;
200 
201  /* Allocate I/O buffer */
202  iobuf = alloc_iob ( MAX_LL_HEADER_LEN + sizeof ( *eapol ) + len );
203  if ( ! iobuf )
204  return -ENOMEM;
205  iob_reserve ( iobuf, MAX_LL_HEADER_LEN );
206 
207  /* Construct EAPoL header */
208  eapol = iob_put ( iobuf, sizeof ( *eapol ) );
209  eapol->version = EAPOL_VERSION_2001;
210  eapol->type = type;
211  eapol->len = htons ( len );
212 
213  /* Append packet body */
214  memcpy ( iob_put ( iobuf, len ), data, len );
215 
216  /* Transmit packet */
217  if ( ( rc = net_tx ( iob_disown ( iobuf ), netdev, &eapol_protocol,
218  &eapol_mac, netdev->ll_addr ) ) != 0 ) {
219  DBGC ( netdev, "EAPOL %s could not transmit type %d: %s\n",
220  netdev->name, type, strerror ( rc ) );
221  DBGC_HDA ( netdev, 0, data, len );
222  return rc;
223  }
224 
225  return 0;
226 }
227 
228 /**
229  * Transmit EAPoL-encapsulated EAP packet
230  *
231  * @v supplicant EAPoL supplicant
232  * @v ll_source Link-layer source address
233  * @ret rc Return status code
234  */
235 static int eapol_eap_tx ( struct eap_supplicant *eap, const void *data,
236  size_t len ) {
237  struct eapol_supplicant *supplicant =
238  container_of ( eap, struct eapol_supplicant, eap );
239 
240  /* Transmit encapsulated packet */
241  return eapol_tx ( supplicant, EAPOL_TYPE_EAP, data, len );
242 }
243 
244 /**
245  * (Re)transmit EAPoL-Start packet
246  *
247  * @v timer EAPoL-Start timer
248  * @v expired Failure indicator
249  */
250 static void eapol_expired ( struct retry_timer *timer, int fail __unused ) {
251  struct eapol_supplicant *supplicant =
253  struct net_device *netdev = supplicant->eap.netdev;
254 
255  /* Stop transmitting after maximum number of attempts */
256  if ( supplicant->count++ >= EAPOL_START_COUNT ) {
257  DBGC ( netdev, "EAPOL %s giving up\n", netdev->name );
258  return;
259  }
260 
261  /* Schedule next transmission */
263 
264  /* Transmit EAPoL-Start, ignoring errors */
265  DBGC2 ( netdev, "EAPOL %s transmitting Start\n", netdev->name );
266  eapol_tx ( supplicant, EAPOL_TYPE_START, NULL, 0 );
267 }
268 
269 /**
270  * Create EAPoL supplicant
271  *
272  * @v netdev Network device
273  * @v priv Private data
274  * @ret rc Return status code
275  */
276 static int eapol_probe ( struct net_device *netdev, void *priv ) {
277  struct eapol_supplicant *supplicant = priv;
279 
280  /* Ignore non-EAPoL devices */
281  if ( ll_protocol->ll_proto != htons ( ARPHRD_ETHER ) )
282  return 0;
283  if ( vlan_tag ( netdev ) )
284  return 0;
285 
286  /* Initialise structure */
287  supplicant->eap.netdev = netdev;
288  supplicant->eap.tx = eapol_eap_tx;
289  timer_init ( &supplicant->timer, eapol_expired, &netdev->refcnt );
290 
291  return 0;
292 }
293 
294 /**
295  * Handle EAPoL supplicant state change
296  *
297  * @v netdev Network device
298  * @v priv Private data
299  */
300 static void eapol_notify ( struct net_device *netdev, void *priv ) {
301  struct eapol_supplicant *supplicant = priv;
302 
303  /* Ignore non-EAPoL devices */
304  if ( ! supplicant->eap.netdev )
305  return;
306 
307  /* Terminate and reset EAP when link goes down */
308  if ( ! ( netdev_is_open ( netdev ) && netdev_link_ok ( netdev ) ) ) {
309  if ( timer_running ( &supplicant->timer ) ) {
310  DBGC ( netdev, "EAPOL %s shutting down\n",
311  netdev->name );
312  }
313  supplicant->eap.flags = 0;
314  stop_timer ( &supplicant->timer );
315  return;
316  }
317 
318  /* Do nothing if EAP is already in progress */
319  if ( timer_running ( &supplicant->timer ) )
320  return;
321 
322  /* Do nothing if EAP has already finished transmitting */
323  if ( supplicant->eap.flags & EAP_FL_PASSIVE )
324  return;
325 
326  /* Otherwise, start sending EAPoL-Start */
327  start_timer_nodelay ( &supplicant->timer );
328  supplicant->count = 0;
329  DBGC ( netdev, "EAPOL %s starting up\n", netdev->name );
330 }
331 
332 /** EAPoL driver */
333 struct net_driver eapol_driver __net_driver = {
334  .name = "EAPoL",
335  .priv_len = sizeof ( struct eapol_supplicant ),
336  .probe = eapol_probe,
337  .notify = eapol_notify,
338 };
#define iob_pull(iobuf, len)
Definition: iobuf.h:106
uint16_t len
Payload length.
Definition: eapol.h:24
#define EINVAL
Invalid argument.
Definition: errno.h:428
struct arbelprm_rc_send_wqe rc
Definition: arbel.h:14
const char * name
Protocol name.
Definition: netdevice.h:66
struct net_driver eapol_driver __net_driver
EAPoL driver.
Definition: eapol.c:45
#define iob_put(iobuf, len)
Definition: iobuf.h:124
static void start_timer_nodelay(struct retry_timer *timer)
Start timer with no delay.
Definition: retry.h:99
struct net_protocol eapol_protocol __net_protocol
EAPoL protocol.
Definition: eapol.c:125
static int eapol_eap_rx(struct eapol_supplicant *supplicant, struct io_buffer *iobuf, const void *ll_source __unused)
Process EAPoL-encapsulated EAP packet.
Definition: eapol.c:138
static int eapol_eap_tx(struct eap_supplicant *eap, const void *data, size_t len)
Transmit EAPoL-encapsulated EAP packet.
Definition: eapol.c:235
Error codes.
FILE_LICENCE(GPL2_OR_LATER_OR_UBDL)
I/O buffers.
void free_iob(struct io_buffer *iobuf)
Free I/O buffer.
Definition: iobuf.c:152
uint32_t type
Operating system type.
Definition: ena.h:12
Retry timers.
#define EAPOL_START_INTERVAL
Delay between EAPoL-Start packets.
Definition: eapol.h:50
#define DBGC(...)
Definition: compiler.h:505
A retry timer.
Definition: retry.h:21
static int eapol_rx(struct io_buffer *iobuf, struct net_device *netdev, const void *ll_dest __unused, const void *ll_source, unsigned int flags __unused)
Process EAPoL packet.
Definition: eapol.c:62
An EAP supplicant.
Definition: eap.h:138
static void eapol_expired(struct retry_timer *timer, int fail __unused)
(Re)transmit EAPoL-Start packet
Definition: eapol.c:250
int eap_rx(struct eap_supplicant *supplicant, const void *data, size_t len)
Handle EAP packet.
Definition: eap.c:263
#define ntohs(value)
Definition: byteswap.h:136
#define EAPOL_HANDLERS
EAPoL handler table.
Definition: eapol.h:74
struct io_buffer * alloc_iob(size_t len)
Allocate I/O buffer.
Definition: iobuf.c:130
A network upper-layer driver.
Definition: netdevice.h:476
A link-layer protocol.
Definition: netdevice.h:114
Address Resolution Protocol constants and types.
#define ENOTSUP
Operation not supported.
Definition: errno.h:589
A timer.
Definition: timer.h:28
static const uint8_t eapol_mac[ETH_ALEN]
EAPoL destination MAC address.
Definition: eapol.c:48
#define ENOMEM
Not enough space.
Definition: errno.h:534
#define iob_disown(iobuf)
Disown an I/O buffer.
Definition: iobuf.h:216
void * memcpy(void *dest, const void *src, size_t len) __nonnull
const char * name
Name.
Definition: netdevice.h:478
EAPoL header.
Definition: eapol.h:18
static int netdev_is_open(struct net_device *netdev)
Check whether or not network device is open.
Definition: netdevice.h:661
Assertions.
assert((readw(&hdr->flags) &(GTF_reading|GTF_writing))==0)
#define container_of(ptr, type, field)
Get containing structure.
Definition: stddef.h:35
void * netdev_priv(struct net_device *netdev, struct net_driver *driver)
Get network device driver private data.
Definition: netdevice.c:152
#define DBGC_HDA(...)
Definition: compiler.h:506
#define __unused
Declare a variable or data structure as unused.
Definition: compiler.h:573
ring len
Length.
Definition: dwmac.h:231
static int netdev_link_ok(struct net_device *netdev)
Check link state of network device.
Definition: netdevice.h:639
struct eap_supplicant eap
EAP supplicant.
Definition: eapol.h:42
static struct net_device * netdev
Definition: gdbudp.c:52
#define EAP_FL_PASSIVE
EAP supplicant is passive.
Definition: eap.h:174
#define MAX_LL_HEADER_LEN
Maximum length of a link-layer header.
Definition: netdevice.h:45
Extensible Authentication Protocol over LAN (EAPoL)
#define EAP_FL_ONGOING
EAP authentication is in progress.
Definition: eap.h:164
An EAPoL handler.
Definition: eapol.h:56
static void eapol_notify(struct net_device *netdev, void *priv)
Handle EAPoL supplicant state change.
Definition: eapol.c:300
#define iob_unput(iobuf, len)
Definition: iobuf.h:139
uint8_t flags
Flags.
Definition: ena.h:18
char * strerror(int errno)
Retrieve string representation of error number.
Definition: strerror.c:78
struct refcnt refcnt
Reference counter.
Definition: netdevice.h:354
static size_t iob_len(struct io_buffer *iobuf)
Calculate length of data in an I/O buffer.
Definition: iobuf.h:159
#define EAPOL_VERSION_2001
802.1X-2001
Definition: eapol.h:28
#define for_each_table_entry(pointer, table)
Iterate through all entries within a linker table.
Definition: tables.h:385
A network device.
Definition: netdevice.h:352
unsigned char uint8_t
Definition: stdint.h:10
#define EAPOL_TYPE_EAP
EAPoL-encapsulated EAP packets.
Definition: eapol.h:31
#define ETH_ALEN
Definition: if_ether.h:8
#define EAPOL_TYPE_START
EAPoL start.
Definition: eapol.h:34
int(* rx)(struct eapol_supplicant *supplicant, struct io_buffer *iobuf, const void *ll_source)
Process received packet.
Definition: eapol.h:69
uint16_t ll_proto
Link-layer protocol.
Definition: netdevice.h:194
uint8_t type
Type.
Definition: eapol.h:22
A network-layer protocol.
Definition: netdevice.h:64
Network device management.
void start_timer_fixed(struct retry_timer *timer, unsigned long timeout)
Start timer with a specified timeout.
Definition: retry.c:64
#define iob_reserve(iobuf, len)
Definition: iobuf.h:71
void stop_timer(struct retry_timer *timer)
Stop timer.
Definition: retry.c:117
char name[NETDEV_NAME_LEN]
Name of this network device.
Definition: netdevice.h:362
int(* tx)(struct eap_supplicant *supplicant, const void *data, size_t len)
Transmit EAP response.
Definition: eap.h:155
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:1073
#define DBGC2(...)
Definition: compiler.h:522
static struct tlan_private * priv
Definition: tlan.c:225
#define ENOTTY
Inappropriate I/O control operation.
Definition: errno.h:594
struct retry_timer timer
EAPoL-Start retransmission timer.
Definition: eapol.h:44
void * data
Start of data.
Definition: iobuf.h:52
uint8_t version
Version.
Definition: eapol.h:20
static int eapol_probe(struct net_device *netdev, void *priv)
Create EAPoL supplicant.
Definition: eapol.c:276
#define EAP_WAIT_TIMEOUT
EAP protocol wait timeout.
Definition: eap.h:135
uint8_t data[48]
Additional event data.
Definition: ena.h:22
Virtual LANs.
unsigned int count
EAPoL-Start transmission count.
Definition: eapol.h:46
struct net_device * netdev
Network device.
Definition: eap.h:140
An EAPoL supplicant.
Definition: eapol.h:40
#define EAPOL_START_COUNT
Maximum number of EAPoL-Start packets to transmit.
Definition: eapol.h:53
#define ETH_P_EAPOL
Definition: if_ether.h:24
uint8_t ll_addr[MAX_LL_ADDR_LEN]
Link-layer address.
Definition: netdevice.h:387
static unsigned int vlan_tag(struct net_device *netdev)
Get the VLAN tag.
Definition: vlan.h:73
#define ARPHRD_ETHER
Ethernet 10Mbps.
Definition: if_arp.h:16
#define NULL
NULL pointer (VOID *)
Definition: Base.h:321
uint8_t type
Type.
Definition: eapol.h:58
String functions.
#define htons(value)
Definition: byteswap.h:135
static int eapol_tx(struct eapol_supplicant *supplicant, unsigned int type, const void *data, size_t len)
Transmit EAPoL packet.
Definition: eapol.c:194
struct eapol_handler eapol_eap __eapol_handler
EAPoL handler for EAP packets.
Definition: eapol.c:180
struct ll_protocol * ll_protocol
Link-layer protocol.
Definition: netdevice.h:372
Extensible Authentication Protocol.
uint16_t flags
Flags.
Definition: eap.h:142
A persistent I/O buffer.
Definition: iobuf.h:37