aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/include/ipxe/eapol.h8
-rw-r--r--src/net/eapol.c73
2 files changed, 81 insertions, 0 deletions
diff --git a/src/include/ipxe/eapol.h b/src/include/ipxe/eapol.h
index f6009a2..d4ea392 100644
--- a/src/include/ipxe/eapol.h
+++ b/src/include/ipxe/eapol.h
@@ -30,6 +30,9 @@ struct eapol_header {
/** EAPoL-encapsulated EAP packets */
#define EAPOL_TYPE_EAP 0
+/** EAPoL start */
+#define EAPOL_TYPE_START 1
+
/** EAPoL key */
#define EAPOL_TYPE_KEY 5
@@ -37,8 +40,13 @@ struct eapol_header {
struct eapol_supplicant {
/** EAP supplicant */
struct eap_supplicant eap;
+ /** EAPoL-Start retransmission timer */
+ struct retry_timer timer;
};
+/** Delay between EAPoL-Start packets */
+#define EAPOL_START_INTERVAL ( 2 * TICKS_PER_SEC )
+
/** An EAPoL handler */
struct eapol_handler {
/** Type */
diff --git a/src/net/eapol.c b/src/net/eapol.c
index 172037c..1b843e8 100644
--- a/src/net/eapol.c
+++ b/src/net/eapol.c
@@ -31,6 +31,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <ipxe/if_arp.h>
#include <ipxe/netdevice.h>
#include <ipxe/vlan.h>
+#include <ipxe/retry.h>
#include <ipxe/eap.h>
#include <ipxe/eapol.h>
@@ -48,6 +49,37 @@ static const uint8_t eapol_mac[ETH_ALEN] = {
};
/**
+ * Update EAPoL supplicant state
+ *
+ * @v supplicant EAPoL supplicant
+ * @v timeout Timer ticks until next EAPoL-Start (if applicable)
+ */
+static void eapol_update ( struct eapol_supplicant *supplicant,
+ unsigned long timeout ) {
+ struct net_device *netdev = supplicant->eap.netdev;
+
+ /* Check device and EAP state */
+ if ( netdev_is_open ( netdev ) && netdev_link_ok ( netdev ) ) {
+ if ( supplicant->eap.done ) {
+
+ /* EAP has completed: stop sending EAPoL-Start */
+ stop_timer ( &supplicant->timer );
+
+ } else if ( ! timer_running ( &supplicant->timer ) ) {
+
+ /* EAP has not yet begun: start sending EAPoL-Start */
+ start_timer_fixed ( &supplicant->timer, timeout );
+ }
+
+ } else {
+
+ /* Not ready: clear completion and stop sending EAPoL-Start */
+ supplicant->eap.done = 0;
+ stop_timer ( &supplicant->timer );
+ }
+}
+
+/**
* Process EAPoL packet
*
* @v iobuf I/O buffer
@@ -154,6 +186,9 @@ static int eapol_eap_rx ( struct eapol_supplicant *supplicant,
goto drop;
}
+ /* Update supplicant state */
+ eapol_update ( supplicant, EAPOL_START_INTERVAL );
+
drop:
free_iob ( iobuf );
return rc;
@@ -225,6 +260,25 @@ static int eapol_eap_tx ( struct eap_supplicant *eap, const void *data,
}
/**
+ * (Re)transmit EAPoL-Start packet
+ *
+ * @v timer EAPoL-Start timer
+ * @v expired Failure indicator
+ */
+static void eapol_expired ( struct retry_timer *timer, int fail __unused ) {
+ struct eapol_supplicant *supplicant =
+ container_of ( timer, struct eapol_supplicant, timer );
+ struct net_device *netdev = supplicant->eap.netdev;
+
+ /* Schedule next transmission */
+ start_timer_fixed ( timer, EAPOL_START_INTERVAL );
+
+ /* Transmit EAPoL-Start, ignoring errors */
+ DBGC2 ( netdev, "EAPOL %s transmitting Start\n", netdev->name );
+ eapol_tx ( supplicant, EAPOL_TYPE_START, NULL, 0 );
+}
+
+/**
* Create EAPoL supplicant
*
* @v netdev Network device
@@ -244,13 +298,32 @@ static int eapol_probe ( struct net_device *netdev, void *priv ) {
/* Initialise structure */
supplicant->eap.netdev = netdev;
supplicant->eap.tx = eapol_eap_tx;
+ timer_init ( &supplicant->timer, eapol_expired, &netdev->refcnt );
return 0;
}
+/**
+ * Handle EAPoL supplicant state change
+ *
+ * @v netdev Network device
+ * @v priv Private data
+ */
+static void eapol_notify ( struct net_device *netdev __unused, void *priv ) {
+ struct eapol_supplicant *supplicant = priv;
+
+ /* Ignore non-EAPoL devices */
+ if ( ! supplicant->eap.netdev )
+ return;
+
+ /* Update supplicant state */
+ eapol_update ( supplicant, 0 );
+}
+
/** EAPoL driver */
struct net_driver eapol_driver __net_driver = {
.name = "EAPoL",
.priv_len = sizeof ( struct eapol_supplicant ),
.probe = eapol_probe,
+ .notify = eapol_notify,
};