diff --git a/src/config/config_ethernet.c b/src/config/config_ethernet.c index b5f7ddc9d..8a663c923 100644 --- a/src/config/config_ethernet.c +++ b/src/config/config_ethernet.c @@ -46,3 +46,6 @@ REQUIRE_OBJECT ( stp ); #ifdef NET_PROTO_LACP REQUIRE_OBJECT ( eth_slow ); #endif +#ifdef NET_PROTO_EAPOL +REQUIRE_OBJECT ( eapol ); +#endif diff --git a/src/config/general.h b/src/config/general.h index 0c99bcbb6..9edf93b5a 100644 --- a/src/config/general.h +++ b/src/config/general.h @@ -39,6 +39,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #undef NET_PROTO_FCOE /* Fibre Channel over Ethernet protocol */ #define NET_PROTO_STP /* Spanning Tree protocol */ #define NET_PROTO_LACP /* Link Aggregation control protocol */ +#define NET_PROTO_EAPOL /* EAP over LAN protocol */ /* * PXE support diff --git a/src/include/ipxe/eap.h b/src/include/ipxe/eap.h new file mode 100644 index 000000000..6fe70189b --- /dev/null +++ b/src/include/ipxe/eap.h @@ -0,0 +1,69 @@ +#ifndef _IPXE_EAP_H +#define _IPXE_EAP_H + +/** @file + * + * Extensible Authentication Protocol + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include +#include +#include + +/** EAP header */ +struct eap_header { + /** Code */ + uint8_t code; + /** Identifier */ + uint8_t id; + /** Length */ + uint16_t len; +} __attribute__ (( packed )); + +/** EAP request */ +#define EAP_CODE_REQUEST 1 + +/** EAP request */ +struct eap_request { + /** Header */ + struct eap_header hdr; + /** Type */ + uint8_t type; +} __attribute__ (( packed )); + +/** EAP identity */ +#define EAP_TYPE_IDENTITY 1 + +/** EAP success */ +#define EAP_CODE_SUCCESS 3 + +/** EAP failure */ +#define EAP_CODE_FAILURE 4 + +/** EAP packet */ +union eap_packet { + /** Header */ + struct eap_header hdr; + /** Request */ + struct eap_request req; +}; + +/** Link block timeout + * + * We mark the link as blocked upon receiving a Request-Identity, on + * the basis that this most likely indicates that the switch will not + * yet be forwarding packets. + * + * There is no way to tell how frequently the Request-Identity packet + * will be retransmitted by the switch. The default value for Cisco + * switches seems to be 30 seconds, so treat the link as blocked for + * 45 seconds. + */ +#define EAP_BLOCK_TIMEOUT ( 45 * TICKS_PER_SEC ) + +extern int eap_rx ( struct net_device *netdev, const void *data, size_t len ); + +#endif /* _IPXE_EAP_H */ diff --git a/src/include/ipxe/eapol.h b/src/include/ipxe/eapol.h index 612dd36e0..952d6c752 100644 --- a/src/include/ipxe/eapol.h +++ b/src/include/ipxe/eapol.h @@ -26,6 +26,9 @@ struct eapol_header { /** 802.1X-2001 */ #define EAPOL_VERSION_2001 1 +/** EAPoL-encapsulated EAP packets */ +#define EAPOL_TYPE_EAP 0 + /** EAPoL key */ #define EAPOL_TYPE_KEY 5 diff --git a/src/include/ipxe/errfile.h b/src/include/ipxe/errfile.h index 3437a5217..d317ce5be 100644 --- a/src/include/ipxe/errfile.h +++ b/src/include/ipxe/errfile.h @@ -287,6 +287,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define ERRFILE_xsigo ( ERRFILE_NET | 0x00480000 ) #define ERRFILE_ntp ( ERRFILE_NET | 0x00490000 ) #define ERRFILE_httpntlm ( ERRFILE_NET | 0x004a0000 ) +#define ERRFILE_eap ( ERRFILE_NET | 0x004b0000 ) #define ERRFILE_image ( ERRFILE_IMAGE | 0x00000000 ) #define ERRFILE_elf ( ERRFILE_IMAGE | 0x00010000 ) diff --git a/src/net/eap.c b/src/net/eap.c new file mode 100644 index 000000000..8d1d540fb --- /dev/null +++ b/src/net/eap.c @@ -0,0 +1,142 @@ +/* + * Copyright (C) 2021 Michael Brown . + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include +#include +#include + +/** @file + * + * Extensible Authentication Protocol + * + */ + +/** + * Handle EAP Request-Identity + * + * @v netdev Network device + * @ret rc Return status code + */ +static int eap_rx_request_identity ( struct net_device *netdev ) { + + /* Treat Request-Identity as blocking the link */ + DBGC ( netdev, "EAP %s Request-Identity blocking link\n", + netdev->name ); + netdev_link_block ( netdev, EAP_BLOCK_TIMEOUT ); + + return 0; +} + +/** + * Handle EAP Request + * + * @v netdev Network device + * @v req EAP request + * @v len Length of EAP request + * @ret rc Return status code + */ +static int eap_rx_request ( struct net_device *netdev, + const struct eap_request *req, size_t len ) { + + /* Sanity check */ + if ( len < sizeof ( *req ) ) { + DBGC ( netdev, "EAP %s underlength request:\n", netdev->name ); + DBGC_HDA ( netdev, 0, req, len ); + return -EINVAL; + } + + /* Handle according to type */ + switch ( req->type ) { + case EAP_TYPE_IDENTITY: + return eap_rx_request_identity ( netdev ); + default: + DBGC ( netdev, "EAP %s requested type %d unknown:\n", + netdev->name, req->type ); + DBGC_HDA ( netdev, 0, req, len ); + return -ENOTSUP; + } +} + +/** + * Handle EAP Success + * + * @v netdev Network device + * @ret rc Return status code + */ +static int eap_rx_success ( struct net_device *netdev ) { + + /* Mark link as unblocked */ + DBGC ( netdev, "EAP %s Success\n", netdev->name ); + netdev_link_unblock ( netdev ); + + return 0; +} + +/** + * Handle EAP Failure + * + * @v netdev Network device + * @ret rc Return status code + */ +static int eap_rx_failure ( struct net_device *netdev ) { + + /* Record error */ + DBGC ( netdev, "EAP %s Failure\n", netdev->name ); + return -EPERM; +} + +/** + * Handle EAP packet + * + * @v netdev Network device + * @v data EAP packet + * @v len Length of EAP packet + * @ret rc Return status code + */ +int eap_rx ( struct net_device *netdev, const void *data, size_t len ) { + const union eap_packet *eap = data; + + /* Sanity check */ + if ( len < sizeof ( eap->hdr ) ) { + DBGC ( netdev, "EAP %s underlength header:\n", netdev->name ); + DBGC_HDA ( netdev, 0, eap, len ); + return -EINVAL; + } + + /* Handle according to code */ + switch ( eap->hdr.code ) { + case EAP_CODE_REQUEST: + return eap_rx_request ( netdev, &eap->req, len ); + case EAP_CODE_SUCCESS: + return eap_rx_success ( netdev ); + case EAP_CODE_FAILURE: + return eap_rx_failure ( netdev ); + default: + DBGC ( netdev, "EAP %s unsupported code %d\n", + netdev->name, eap->hdr.code ); + DBGC_HDA ( netdev, 0, eap, len ); + return -ENOTSUP; + } +} diff --git a/src/net/eapol.c b/src/net/eapol.c index 91119d379..3578f0e37 100644 --- a/src/net/eapol.c +++ b/src/net/eapol.c @@ -29,6 +29,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include #include +#include #include /** @file @@ -102,3 +103,39 @@ struct net_protocol eapol_protocol __net_protocol = { .net_proto = htons ( ETH_P_EAPOL ), .rx = eapol_rx, }; + +/** + * Process EAPoL-encapsulated EAP packet + * + * @v netdev Network device + * @v ll_source Link-layer source address + * @ret rc Return status code + */ +static int eapol_eap_rx ( struct io_buffer *iobuf, struct net_device *netdev, + const void *ll_source __unused ) { + struct eapol_header *eapol; + int rc; + + /* Sanity check */ + assert ( iob_len ( iobuf ) >= sizeof ( *eapol ) ); + + /* Strip EAPoL header */ + eapol = iob_pull ( iobuf, sizeof ( *eapol ) ); + + /* Process EAP packet */ + if ( ( rc = eap_rx ( netdev, iobuf->data, iob_len ( iobuf ) ) ) != 0 ) { + DBGC ( netdev, "EAPOL %s v%d EAP failed: %s\n", + netdev->name, eapol->version, strerror ( rc ) ); + goto drop; + } + + drop: + free_iob ( iobuf ); + return rc; +} + +/** EAPoL handler for EAP packets */ +struct eapol_handler eapol_eap __eapol_handler = { + .type = EAPOL_TYPE_EAP, + .rx = eapol_eap_rx, +};