From 6701d91c5033a3804a3bb5d49a8f9f2212b901b4 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 5 Jul 2023 14:30:54 +0100 Subject: [netdevice] Stop link block timer when device is closed A running link block timer holds a reference to the network device and will prevent it from being freed until the timer expires. It is impossible for free_netdev() to be called while the timer is still running: the call to stop_timer() therein is therefore a no-op. Stop the link block timer when the device is closed, to allow a link-blocked device to be freed immediately upon unregistration of the device. (Since link block state is updated in response to received packets, the state is effectively undefined for a closed device: there is therefore no reason to leave the timer running.) Signed-off-by: Michael Brown --- src/net/netdevice.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'src/net') diff --git a/src/net/netdevice.c b/src/net/netdevice.c index 07961bf20..915178218 100644 --- a/src/net/netdevice.c +++ b/src/net/netdevice.c @@ -656,7 +656,7 @@ static void free_netdev ( struct refcnt *refcnt ) { struct net_device *netdev = container_of ( refcnt, struct net_device, refcnt ); - stop_timer ( &netdev->link_block ); + assert ( ! timer_running ( &netdev->link_block ) ); netdev_tx_flush ( netdev ); netdev_rx_flush ( netdev ); clear_settings ( netdev_settings ( netdev ) ); @@ -879,6 +879,9 @@ void netdev_close ( struct net_device *netdev ) { /* Close the device */ netdev->op->close ( netdev ); + /* Stop link block timer */ + stop_timer ( &netdev->link_block ); + /* Flush TX and RX queues */ netdev_tx_flush ( netdev ); netdev_rx_flush ( netdev ); -- cgit v1.2.3-55-g7522 From d5c08f78bdf45668a955c1f3d3a96f35d8c0e91b Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 19 Jul 2023 11:10:15 +0100 Subject: [ntp] Define NTP server setting Define the IPv4 NTP server setting to simplify the use of a DHCP-provided NTP server in scripts, using e.g. #!ipxe dhcp ntp ${ntp} Signed-off-by: Michael Brown --- src/include/ipxe/dhcp.h | 3 +++ src/net/udp/ntp.c | 10 ++++++++++ 2 files changed, 13 insertions(+) (limited to 'src/net') diff --git a/src/include/ipxe/dhcp.h b/src/include/ipxe/dhcp.h index a1f9ee250..51349efd9 100644 --- a/src/include/ipxe/dhcp.h +++ b/src/include/ipxe/dhcp.h @@ -86,6 +86,9 @@ struct dhcp_packet; /** Maximum transmission unit */ #define DHCP_MTU 26 +/** NTP servers */ +#define DHCP_NTP_SERVERS 42 + /** Vendor encapsulated options */ #define DHCP_VENDOR_ENCAP 43 diff --git a/src/net/udp/ntp.c b/src/net/udp/ntp.c index 11f8ccc00..559233575 100644 --- a/src/net/udp/ntp.c +++ b/src/net/udp/ntp.c @@ -36,6 +36,8 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include #include +#include +#include #include /** @file @@ -273,3 +275,11 @@ int start_ntp ( struct interface *job, const char *hostname ) { err_alloc: return rc; } + +/** IPv4 NTP server setting */ +const struct setting ntp_setting __setting ( SETTING_IP4_EXTRA, ntp ) = { + .name = "ntp", + .description = "NTP server", + .tag = DHCP_NTP_SERVERS, + .type = &setting_type_ipv4, +}; -- cgit v1.2.3-55-g7522 From c1834f323f4f6b9b46cd5895b1457a117381363f Mon Sep 17 00:00:00 2001 From: Cornelius Hoffmann Date: Mon, 10 Jul 2023 22:24:26 +0200 Subject: [dhcp] Request NTP server option Signed-off-by: Michael Brown --- src/net/udp/dhcp.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'src/net') diff --git a/src/net/udp/dhcp.c b/src/net/udp/dhcp.c index bd2c4a197..8e2e97f10 100644 --- a/src/net/udp/dhcp.c +++ b/src/net/udp/dhcp.c @@ -91,9 +91,10 @@ static uint8_t dhcp_request_options_data[] = { DHCP_PARAMETER_REQUEST_LIST, DHCP_OPTION ( DHCP_SUBNET_MASK, DHCP_ROUTERS, DHCP_DNS_SERVERS, DHCP_LOG_SERVERS, DHCP_HOST_NAME, DHCP_DOMAIN_NAME, - DHCP_ROOT_PATH, DHCP_MTU, DHCP_VENDOR_ENCAP, - DHCP_VENDOR_CLASS_ID, DHCP_TFTP_SERVER_NAME, - DHCP_BOOTFILE_NAME, DHCP_DOMAIN_SEARCH, + DHCP_ROOT_PATH, DHCP_MTU, DHCP_NTP_SERVERS, + DHCP_VENDOR_ENCAP, DHCP_VENDOR_CLASS_ID, + DHCP_TFTP_SERVER_NAME, DHCP_BOOTFILE_NAME, + DHCP_DOMAIN_SEARCH, 128, 129, 130, 131, 132, 133, 134, 135, /* for PXE */ DHCP_EB_ENCAP, DHCP_ISCSI_INITIATOR_IQN ), DHCP_END -- cgit v1.2.3-55-g7522 From ae4e85bde97c9b216736a5087039f3309a628d24 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 13 Sep 2023 16:29:59 +0100 Subject: [netdevice] Allocate private data for each network upper-layer driver Allow network upper-layer drivers (such as LLDP, which attaches to each network device in order to provide a corresponding LLDP settings block) to specify a size for private data, which will be allocated as part of the network device structure (as with the existing private data allocated for the underlying device driver). This will allow network upper-layer drivers to be simplified by omitting memory allocation and freeing code. If the upper-layer driver requires a reference counter (e.g. for interface initialisation), then it may use the network device's existing reference counter, since this is now the reference counter for the containing block of memory. Signed-off-by: Michael Brown --- src/arch/x86/interface/pxe/pxe_call.c | 3 +- src/arch/x86/interface/vmware/guestinfo.c | 8 +++- src/core/cachedhcp.c | 3 +- src/drivers/net/netfront.c | 4 +- src/include/ipxe/netdevice.h | 13 ++++-- src/interface/efi/efi_snp.c | 9 ++-- src/net/fcoe.c | 9 ++-- src/net/infiniband/xsigo.c | 4 +- src/net/ipv6.c | 4 +- src/net/lldp.c | 3 +- src/net/neighbour.c | 3 +- src/net/netdevice.c | 72 +++++++++++++++++++++++++------ src/net/vlan.c | 9 ++-- 13 files changed, 110 insertions(+), 34 deletions(-) (limited to 'src/net') diff --git a/src/arch/x86/interface/pxe/pxe_call.c b/src/arch/x86/interface/pxe/pxe_call.c index 671182991..0e8d5c5a8 100644 --- a/src/arch/x86/interface/pxe/pxe_call.c +++ b/src/arch/x86/interface/pxe/pxe_call.c @@ -375,9 +375,10 @@ int pxe_start_nbp ( void ) { * Notify BIOS of existence of network device * * @v netdev Network device + * @v priv Private data * @ret rc Return status code */ -static int pxe_notify ( struct net_device *netdev ) { +static int pxe_notify ( struct net_device *netdev, void *priv __unused ) { /* Do nothing if we already have a network device */ if ( pxe_netdev ) diff --git a/src/arch/x86/interface/vmware/guestinfo.c b/src/arch/x86/interface/vmware/guestinfo.c index a0530c8d1..b52c2e87b 100644 --- a/src/arch/x86/interface/vmware/guestinfo.c +++ b/src/arch/x86/interface/vmware/guestinfo.c @@ -207,9 +207,11 @@ struct init_fn guestinfo_init_fn __init_fn ( INIT_NORMAL ) = { * Create per-netdevice GuestInfo settings * * @v netdev Network device + * @v priv Private data * @ret rc Return status code */ -static int guestinfo_net_probe ( struct net_device *netdev ) { +static int guestinfo_net_probe ( struct net_device *netdev, + void *priv __unused ) { struct settings *settings; int rc; @@ -247,8 +249,10 @@ static int guestinfo_net_probe ( struct net_device *netdev ) { * Remove per-netdevice GuestInfo settings * * @v netdev Network device + * @v priv Private data */ -static void guestinfo_net_remove ( struct net_device *netdev ) { +static void guestinfo_net_remove ( struct net_device *netdev, + void *priv __unused ) { struct settings *parent = netdev_settings ( netdev ); struct settings *settings; diff --git a/src/core/cachedhcp.c b/src/core/cachedhcp.c index 60213f02d..57226e165 100644 --- a/src/core/cachedhcp.c +++ b/src/core/cachedhcp.c @@ -295,9 +295,10 @@ struct startup_fn cachedhcp_startup_fn __startup_fn ( STARTUP_LATE ) = { * Apply cached DHCPACK to network device, if applicable * * @v netdev Network device + * @v priv Private data * @ret rc Return status code */ -static int cachedhcp_probe ( struct net_device *netdev ) { +static int cachedhcp_probe ( struct net_device *netdev, void *priv __unused ) { /* Apply cached DHCPACK to network device, if applicable */ return cachedhcp_apply ( &cached_dhcpack, netdev ); diff --git a/src/drivers/net/netfront.c b/src/drivers/net/netfront.c index 90930a5a3..12713c5b4 100644 --- a/src/drivers/net/netfront.c +++ b/src/drivers/net/netfront.c @@ -1056,9 +1056,11 @@ struct xen_driver netfront_driver __xen_driver = { * Inhibit emulated PCI devices * * @v netdev Network device + * @v priv Private data * @ret rc Return status code */ -static int netfront_net_probe ( struct net_device *netdev ) { +static int netfront_net_probe ( struct net_device *netdev, + void *priv __unused ) { struct netfront_nic *netfront; /* Inhibit emulated PCI devices matching an existing netfront device */ diff --git a/src/include/ipxe/netdevice.h b/src/include/ipxe/netdevice.h index a65dbfd23..caa83b44b 100644 --- a/src/include/ipxe/netdevice.h +++ b/src/include/ipxe/netdevice.h @@ -473,22 +473,27 @@ struct net_device { struct net_driver { /** Name */ const char *name; + /** Size of private data */ + size_t priv_len; /** Probe device * * @v netdev Network device + * @v priv Private data * @ret rc Return status code */ - int ( * probe ) ( struct net_device *netdev ); + int ( * probe ) ( struct net_device *netdev, void *priv ); /** Notify of device or link state change * * @v netdev Network device + * @v priv Private data */ - void ( * notify ) ( struct net_device *netdev ); + void ( * notify ) ( struct net_device *netdev, void *priv ); /** Remove device * * @v netdev Network device + * @v priv Private data */ - void ( * remove ) ( struct net_device *netdev ); + void ( * remove ) ( struct net_device *netdev, void *priv ); }; /** Network driver table */ @@ -688,6 +693,8 @@ netdev_rx_frozen ( struct net_device *netdev ) { return ( netdev->state & NETDEV_RX_FROZEN ); } +extern void * netdev_priv ( struct net_device *netdev, + struct net_driver *driver ); extern void netdev_rx_freeze ( struct net_device *netdev ); extern void netdev_rx_unfreeze ( struct net_device *netdev ); extern void netdev_link_err ( struct net_device *netdev, int rc ); diff --git a/src/interface/efi/efi_snp.c b/src/interface/efi/efi_snp.c index c4f7d4ea8..8443be997 100644 --- a/src/interface/efi/efi_snp.c +++ b/src/interface/efi/efi_snp.c @@ -1777,9 +1777,10 @@ static struct efi_snp_device * efi_snp_demux ( struct net_device *netdev ) { * Create SNP device * * @v netdev Network device + * @v priv Private data * @ret rc Return status code */ -static int efi_snp_probe ( struct net_device *netdev ) { +static int efi_snp_probe ( struct net_device *netdev, void *priv __unused ) { EFI_BOOT_SERVICES *bs = efi_systab->BootServices; struct efi_device *efidev; struct efi_snp_device *snpdev; @@ -2017,8 +2018,9 @@ static int efi_snp_probe ( struct net_device *netdev ) { * Handle SNP device or link state change * * @v netdev Network device + * @v priv Private data */ -static void efi_snp_notify ( struct net_device *netdev ) { +static void efi_snp_notify ( struct net_device *netdev, void *priv __unused ) { struct efi_snp_device *snpdev; /* Locate SNP device */ @@ -2042,8 +2044,9 @@ static void efi_snp_notify ( struct net_device *netdev ) { * Destroy SNP device * * @v netdev Network device + * @v priv Private data */ -static void efi_snp_remove ( struct net_device *netdev ) { +static void efi_snp_remove ( struct net_device *netdev, void *priv __unused ) { EFI_BOOT_SERVICES *bs = efi_systab->BootServices; struct efi_snp_device *snpdev; int leak = efi_shutdown_in_progress; diff --git a/src/net/fcoe.c b/src/net/fcoe.c index f910eeead..70804dd03 100644 --- a/src/net/fcoe.c +++ b/src/net/fcoe.c @@ -1110,9 +1110,10 @@ static void fcoe_expired ( struct retry_timer *timer, int over __unused ) { * Create FCoE port * * @v netdev Network device + * @v priv Private data * @ret rc Return status code */ -static int fcoe_probe ( struct net_device *netdev ) { +static int fcoe_probe ( struct net_device *netdev, void *priv __unused ) { struct ll_protocol *ll_protocol = netdev->ll_protocol; struct fcoe_port *fcoe; int rc; @@ -1162,8 +1163,9 @@ static int fcoe_probe ( struct net_device *netdev ) { * Handle FCoE port device or link state change * * @v netdev Network device + * @v priv Private data */ -static void fcoe_notify ( struct net_device *netdev ) { +static void fcoe_notify ( struct net_device *netdev, void *priv __unused ) { struct fcoe_port *fcoe; /* Sanity check */ @@ -1185,8 +1187,9 @@ static void fcoe_notify ( struct net_device *netdev ) { * Destroy FCoE port * * @v netdev Network device + * @v priv Private data */ -static void fcoe_remove ( struct net_device *netdev ) { +static void fcoe_remove ( struct net_device *netdev, void *priv __unused ) { struct fcoe_port *fcoe; /* Sanity check */ diff --git a/src/net/infiniband/xsigo.c b/src/net/infiniband/xsigo.c index 4f5c618d7..5e805fa02 100644 --- a/src/net/infiniband/xsigo.c +++ b/src/net/infiniband/xsigo.c @@ -1829,8 +1829,10 @@ struct ib_driver xsigo_ib_driver __ib_driver = { * Handle device or link status change * * @v netdev Network device + * @v priv Private data */ -static void xsigo_net_notify ( struct net_device *netdev ) { +static void xsigo_net_notify ( struct net_device *netdev, + void *priv __unused ) { struct xsigo_device *xdev; struct ib_device *ibdev; struct xsigo_manager *xcm; diff --git a/src/net/ipv6.c b/src/net/ipv6.c index ef5e51daa..a0173dfb1 100644 --- a/src/net/ipv6.c +++ b/src/net/ipv6.c @@ -1224,9 +1224,11 @@ struct ipv6_settings { * Register IPv6 link-local address settings * * @v netdev Network device + * @v priv Private data * @ret rc Return status code */ -static int ipv6_register_settings ( struct net_device *netdev ) { +static int ipv6_register_settings ( struct net_device *netdev, + void *priv __unused ) { struct settings *parent = netdev_settings ( netdev ); struct ipv6_settings *ipv6set; int rc; diff --git a/src/net/lldp.c b/src/net/lldp.c index 72e3ecdf6..2ef32cb0c 100644 --- a/src/net/lldp.c +++ b/src/net/lldp.c @@ -296,9 +296,10 @@ struct net_protocol lldp_protocol __net_protocol = { * Create LLDP settings block * * @v netdev Network device + * @v priv Private data * @ret rc Return status code */ -static int lldp_probe ( struct net_device *netdev ) { +static int lldp_probe ( struct net_device *netdev, void *priv __unused ) { struct lldp_settings *lldpset; int rc; diff --git a/src/net/neighbour.c b/src/net/neighbour.c index 7f66d9992..13a8bc3ba 100644 --- a/src/net/neighbour.c +++ b/src/net/neighbour.c @@ -383,8 +383,9 @@ int neighbour_define ( struct net_device *netdev, * Update neighbour cache on network device state change or removal * * @v netdev Network device + * @v priv Private data */ -static void neighbour_flush ( struct net_device *netdev ) { +static void neighbour_flush ( struct net_device *netdev, void *priv __unused ) { struct neighbour *neighbour; struct neighbour *tmp; diff --git a/src/net/netdevice.c b/src/net/netdevice.c index 915178218..a9ed18134 100644 --- a/src/net/netdevice.c +++ b/src/net/netdevice.c @@ -109,6 +109,51 @@ static int netdev_has_ll_addr ( struct net_device *netdev ) { return 0; } +/** + * Get offset of network device driver private data + * + * @v driver Upper-layer driver, or NULL for device driver + * @ret offset Offset of driver private data + */ +static size_t netdev_priv_offset ( struct net_driver *driver ) { + struct net_device *netdev; + unsigned int num_configs; + size_t offset; + + /* Allow space for network device */ + offset = sizeof ( *netdev ); + + /* Allow space for configurations */ + num_configs = table_num_entries ( NET_DEVICE_CONFIGURATORS ); + offset += ( num_configs * sizeof ( netdev->configs[0] ) ); + + /* Place variable-length device driver private data at end */ + if ( ! driver ) + driver = table_end ( NET_DRIVERS ); + + /* Allow space for preceding upper-layer drivers' private data */ + for_each_table_entry_continue_reverse ( driver, NET_DRIVERS ) { + offset += driver->priv_len; + } + + /* Sanity check */ + assert ( ( offset & ( sizeof ( void * ) - 1 ) ) == 0 ); + + return offset; +} + +/** + * Get network device driver private data + * + * @v netdev Network device + * @v driver Upper-layer driver, or NULL for device driver + * @ret priv Driver private data + */ +void * netdev_priv ( struct net_device *netdev, struct net_driver *driver ) { + + return ( ( ( void * ) netdev ) + netdev_priv_offset ( driver ) ); +} + /** * Notify drivers of network device or link state change * @@ -116,10 +161,12 @@ static int netdev_has_ll_addr ( struct net_device *netdev ) { */ static void netdev_notify ( struct net_device *netdev ) { struct net_driver *driver; + void *priv; for_each_table_entry ( driver, NET_DRIVERS ) { + priv = netdev_priv ( netdev, driver ); if ( driver->notify ) - driver->notify ( netdev ); + driver->notify ( netdev, priv ); } } @@ -675,14 +722,8 @@ struct net_device * alloc_netdev ( size_t priv_len ) { struct net_device *netdev; struct net_device_configurator *configurator; struct net_device_configuration *config; - unsigned int num_configs; - size_t confs_len; - size_t total_len; - num_configs = table_num_entries ( NET_DEVICE_CONFIGURATORS ); - confs_len = ( num_configs * sizeof ( netdev->configs[0] ) ); - total_len = ( sizeof ( *netdev ) + confs_len + priv_len ); - netdev = zalloc ( total_len ); + netdev = zalloc ( netdev_priv_offset ( NULL ) + priv_len ); if ( netdev ) { ref_init ( &netdev->refcnt, free_netdev ); netdev->link_rc = -EUNKNOWN_LINK_STATUS; @@ -701,8 +742,7 @@ struct net_device * alloc_netdev ( size_t priv_len ) { &netdev->refcnt ); config++; } - netdev->priv = ( ( ( void * ) netdev ) + sizeof ( *netdev ) + - confs_len ); + netdev->priv = netdev_priv ( netdev, NULL ); } return netdev; } @@ -722,6 +762,7 @@ int register_netdev ( struct net_device *netdev ) { struct net_device *duplicate; unsigned int i; uint32_t seed; + void *priv; int rc; /* Set initial link-layer address, if not already set */ @@ -784,7 +825,9 @@ int register_netdev ( struct net_device *netdev ) { /* Probe device */ for_each_table_entry ( driver, NET_DRIVERS ) { - if ( driver->probe && ( rc = driver->probe ( netdev ) ) != 0 ) { + priv = netdev_priv ( netdev, driver ); + if ( driver->probe && + ( rc = driver->probe ( netdev, priv ) ) != 0 ) { DBGC ( netdev, "NETDEV %s could not add %s device: " "%s\n", netdev->name, driver->name, strerror ( rc ) ); @@ -796,8 +839,9 @@ int register_netdev ( struct net_device *netdev ) { err_probe: for_each_table_entry_continue_reverse ( driver, NET_DRIVERS ) { + priv = netdev_priv ( netdev, driver ); if ( driver->remove ) - driver->remove ( netdev ); + driver->remove ( netdev, priv ); } clear_settings ( netdev_settings ( netdev ) ); unregister_settings ( netdev_settings ( netdev ) ); @@ -896,14 +940,16 @@ void netdev_close ( struct net_device *netdev ) { */ void unregister_netdev ( struct net_device *netdev ) { struct net_driver *driver; + void *priv; /* Ensure device is closed */ netdev_close ( netdev ); /* Remove device */ for_each_table_entry_reverse ( driver, NET_DRIVERS ) { + priv = netdev_priv ( netdev, driver ); if ( driver->remove ) - driver->remove ( netdev ); + driver->remove ( netdev, priv ); } /* Unregister per-netdev configuration settings */ diff --git a/src/net/vlan.c b/src/net/vlan.c index d73a95711..c61bb850e 100644 --- a/src/net/vlan.c +++ b/src/net/vlan.c @@ -470,9 +470,10 @@ void vlan_auto ( const void *ll_addr, unsigned int tag ) { * Create automatic VLAN device * * @v trunk Trunk network device + * @v priv Private data * @ret rc Return status code */ -static int vlan_probe ( struct net_device *trunk ) { +static int vlan_probe ( struct net_device *trunk, void *priv __unused ) { int rc; /* Do nothing unless an automatic VLAN exists */ @@ -498,8 +499,9 @@ static int vlan_probe ( struct net_device *trunk ) { * Handle trunk network device link state change * * @v trunk Trunk network device + * @v priv Private data */ -static void vlan_notify ( struct net_device *trunk ) { +static void vlan_notify ( struct net_device *trunk, void *priv __unused ) { struct net_device *netdev; struct vlan_device *vlan; @@ -538,8 +540,9 @@ static int vlan_remove_first ( struct net_device *trunk ) { * Destroy all VLAN devices for a given trunk * * @v trunk Trunk network device + * @v priv Private data */ -static void vlan_remove ( struct net_device *trunk ) { +static void vlan_remove ( struct net_device *trunk, void *priv __unused ) { /* Remove all VLAN devices attached to this trunk, safe * against arbitrary net device removal. -- cgit v1.2.3-55-g7522 From cc1e27e525201f5ee7fcc098f47f04ec26814289 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 13 Sep 2023 20:23:59 +0100 Subject: [lldp] Use driver-private data to hold LLDP settings block Simplify the LLDP code by using driver-private data to hold the LLDP settings block, instead of using a separate allocation. This avoids the need to maintain a list of LLDP settings blocks (since the LLDP settings block pointer can always be obtained using netdev_priv()) and obviates several failure paths. Any recorded LLDP data is now freed when the network device is unregistered, since there is no longer a dedicated reference counter for the LLDP settings block. To minimise surprise, we also now explicitly unregister the settings block. This is not strictly necessary (since the block will be automatically unregistered when the parent network device settings block is unregistered), but it maintains symmetry between lldp_probe() and lldp_remove(). The overall reduction in the size of the LLDP code is around 15%. Signed-off-by: Michael Brown --- src/net/lldp.c | 94 +++++++++++++++++++--------------------------------------- 1 file changed, 31 insertions(+), 63 deletions(-) (limited to 'src/net') diff --git a/src/net/lldp.c b/src/net/lldp.c index 2ef32cb0c..a854d0ace 100644 --- a/src/net/lldp.c +++ b/src/net/lldp.c @@ -40,12 +40,8 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** An LLDP settings block */ struct lldp_settings { - /** Reference counter */ - struct refcnt refcnt; /** Settings interface */ struct settings settings; - /** List of LLDP settings blocks */ - struct list_head list; /** Name */ const char *name; /** LLDP data */ @@ -54,45 +50,12 @@ struct lldp_settings { size_t len; }; +/* Forward declaration */ +struct net_driver lldp_driver __net_driver; + /** LLDP settings scope */ static const struct settings_scope lldp_settings_scope; -/** List of LLDP settings blocks */ -static LIST_HEAD ( lldp_settings ); - -/** - * Free LLDP settings block - * - * @v refcnt Reference counter - */ -static void lldp_free ( struct refcnt *refcnt ) { - struct lldp_settings *lldpset = - container_of ( refcnt, struct lldp_settings, refcnt ); - - DBGC ( lldpset, "LLDP %s freed\n", lldpset->name ); - list_del ( &lldpset->list ); - free ( lldpset->data ); - free ( lldpset ); -} - -/** - * Find LLDP settings block - * - * @v netdev Network device - * @ret lldpset LLDP settings block - */ -static struct lldp_settings * lldp_find ( struct net_device *netdev ) { - struct lldp_settings *lldpset; - - /* Find matching LLDP settings block */ - list_for_each_entry ( lldpset, &lldp_settings, list ) { - if ( netdev_settings ( netdev ) == lldpset->settings.parent ) - return lldpset; - } - - return NULL; -} - /** * Check applicability of LLDP setting * @@ -246,13 +209,7 @@ static int lldp_rx ( struct io_buffer *iobuf, struct net_device *netdev, int rc; /* Find matching LLDP settings block */ - lldpset = lldp_find ( netdev ); - if ( ! lldpset ) { - DBGC ( netdev, "LLDP %s has no \"%s\" settings block\n", - netdev->name, LLDP_SETTINGS_NAME ); - rc = -ENOENT; - goto err_find; - } + lldpset = netdev_priv ( netdev, &lldp_driver ); /* Create trimmed copy of received LLDP data */ len = iob_len ( iobuf ); @@ -280,7 +237,6 @@ static int lldp_rx ( struct io_buffer *iobuf, struct net_device *netdev, free ( data ); err_alloc: - err_find: free_iob ( iobuf ); return rc; } @@ -299,24 +255,18 @@ struct net_protocol lldp_protocol __net_protocol = { * @v priv Private data * @ret rc Return status code */ -static int lldp_probe ( struct net_device *netdev, void *priv __unused ) { - struct lldp_settings *lldpset; +static int lldp_probe ( struct net_device *netdev, void *priv ) { + struct lldp_settings *lldpset = priv; int rc; - /* Allocate LLDP settings block */ - lldpset = zalloc ( sizeof ( *lldpset ) ); - if ( ! lldpset ) { - rc = -ENOMEM; - goto err_alloc; - } - ref_init ( &lldpset->refcnt, lldp_free ); + /* Initialise LLDP settings block */ settings_init ( &lldpset->settings, &lldp_settings_operations, - &lldpset->refcnt, &lldp_settings_scope ); - list_add_tail ( &lldpset->list, &lldp_settings ); + &netdev->refcnt, &lldp_settings_scope ); lldpset->name = netdev->name; /* Register settings */ - if ( ( rc = register_settings ( &lldpset->settings, netdev_settings ( netdev ), + if ( ( rc = register_settings ( &lldpset->settings, + netdev_settings ( netdev ), LLDP_SETTINGS_NAME ) ) != 0 ) { DBGC ( lldpset, "LLDP %s could not register settings: %s\n", lldpset->name, strerror ( rc ) ); @@ -324,18 +274,36 @@ static int lldp_probe ( struct net_device *netdev, void *priv __unused ) { } DBGC ( lldpset, "LLDP %s registered\n", lldpset->name ); - ref_put ( &lldpset->refcnt ); return 0; unregister_settings ( &lldpset->settings ); err_register: - ref_put ( &lldpset->refcnt ); - err_alloc: + assert ( lldpset->data == NULL ); return rc; } +/** + * Remove LLDP settings block + * + * @v netdev Network device + * @v priv Private data + */ +static void lldp_remove ( struct net_device *netdev __unused, void *priv ) { + struct lldp_settings *lldpset = priv; + + /* Unregister settings */ + unregister_settings ( &lldpset->settings ); + DBGC ( lldpset, "LLDP %s unregistered\n", lldpset->name ); + + /* Free any LLDP data */ + free ( lldpset->data ); + lldpset->data = NULL; +} + /** LLDP driver */ struct net_driver lldp_driver __net_driver = { .name = "LLDP", + .priv_len = sizeof ( struct lldp_settings ), .probe = lldp_probe, + .remove = lldp_remove, }; -- cgit v1.2.3-55-g7522 From 8b1d34badf321668f830c78e6803d718446b57ef Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 13 Sep 2023 22:21:08 +0100 Subject: [ipv6] Use driver-private data to hold link-local IPv6 settings block Simplify the IPv6 link-local settings code by using driver-private data to hold the settings block, instead of using a separate allocation. Signed-off-by: Michael Brown --- src/net/ipv6.c | 39 ++++++++++----------------------------- 1 file changed, 10 insertions(+), 29 deletions(-) (limited to 'src/net') diff --git a/src/net/ipv6.c b/src/net/ipv6.c index a0173dfb1..8ee0804d3 100644 --- a/src/net/ipv6.c +++ b/src/net/ipv6.c @@ -1212,14 +1212,6 @@ static struct settings_operations ipv6_settings_operations = { .fetch = ipv6_fetch, }; -/** IPv6 link-local address settings */ -struct ipv6_settings { - /** Reference counter */ - struct refcnt refcnt; - /** Settings interface */ - struct settings settings; -}; - /** * Register IPv6 link-local address settings * @@ -1227,37 +1219,26 @@ struct ipv6_settings { * @v priv Private data * @ret rc Return status code */ -static int ipv6_register_settings ( struct net_device *netdev, - void *priv __unused ) { +static int ipv6_register_settings ( struct net_device *netdev, void *priv ) { struct settings *parent = netdev_settings ( netdev ); - struct ipv6_settings *ipv6set; + struct settings *settings = priv; int rc; - /* Allocate and initialise structure */ - ipv6set = zalloc ( sizeof ( *ipv6set ) ); - if ( ! ipv6set ) { - rc = -ENOMEM; - goto err_alloc; - } - ref_init ( &ipv6set->refcnt, NULL ); - settings_init ( &ipv6set->settings, &ipv6_settings_operations, - &ipv6set->refcnt, &ipv6_settings_scope ); - ipv6set->settings.order = IPV6_ORDER_LINK_LOCAL; - - /* Register settings */ - if ( ( rc = register_settings ( &ipv6set->settings, parent, + /* Initialise and register settings */ + settings_init ( settings, &ipv6_settings_operations, + &netdev->refcnt, &ipv6_settings_scope ); + settings->order = IPV6_ORDER_LINK_LOCAL; + if ( ( rc = register_settings ( settings, parent, IPV6_SETTINGS_NAME ) ) != 0 ) - goto err_register; + return rc; - err_register: - ref_put ( &ipv6set->refcnt ); - err_alloc: - return rc; + return 0; } /** IPv6 network device driver */ struct net_driver ipv6_driver __net_driver = { .name = "IPv6", + .priv_len = sizeof ( struct settings ), .probe = ipv6_register_settings, }; -- cgit v1.2.3-55-g7522 From cac3a584dc8acea1522669f1ed16e0979fb92252 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Wed, 13 Sep 2023 23:00:57 +0100 Subject: [fcoe] Use driver-private data to hold FCoE port structure Simplify the FCoE code by using driver-private data to hold the FCoE port for each network device, instead of using a separate allocation. Signed-off-by: Michael Brown --- src/net/fcoe.c | 85 +++++++++++++++------------------------------------------- 1 file changed, 21 insertions(+), 64 deletions(-) (limited to 'src/net') diff --git a/src/net/fcoe.c b/src/net/fcoe.c index 70804dd03..9f3ddf88b 100644 --- a/src/net/fcoe.c +++ b/src/net/fcoe.c @@ -69,10 +69,6 @@ FEATURE ( FEATURE_PROTOCOL, "FCoE", DHCP_EB_FEATURE_FCOE, 1 ); /** An FCoE port */ struct fcoe_port { - /** Reference count */ - struct refcnt refcnt; - /** List of FCoE ports */ - struct list_head list; /** Transport interface */ struct interface transport; /** Network device */ @@ -115,6 +111,7 @@ enum fcoe_flags { FCOE_VLAN_TIMED_OUT = 0x0020, }; +struct net_driver fcoe_driver __net_driver; struct net_protocol fcoe_protocol __net_protocol; struct net_protocol fip_protocol __net_protocol; @@ -152,9 +149,6 @@ static uint8_t default_fcf_mac[ETH_ALEN] = /** Maximum number of missing discovery advertisements */ #define FCOE_MAX_FIP_MISSING_KEEPALIVES 4 -/** List of FCoE ports */ -static LIST_HEAD ( fcoe_ports ); - /****************************************************************************** * * FCoE protocol @@ -162,22 +156,6 @@ static LIST_HEAD ( fcoe_ports ); ****************************************************************************** */ -/** - * Identify FCoE port by network device - * - * @v netdev Network device - * @ret fcoe FCoE port, or NULL - */ -static struct fcoe_port * fcoe_demux ( struct net_device *netdev ) { - struct fcoe_port *fcoe; - - list_for_each_entry ( fcoe, &fcoe_ports, list ) { - if ( fcoe->netdev == netdev ) - return fcoe; - } - return NULL; -} - /** * Reset FCoE port * @@ -348,7 +326,8 @@ static int fcoe_rx ( struct io_buffer *iobuf, struct net_device *netdev, int rc; /* Identify FCoE port */ - if ( ( fcoe = fcoe_demux ( netdev ) ) == NULL ) { + fcoe = netdev_priv ( netdev, &fcoe_driver ); + if ( ! fcoe->netdev ) { DBG ( "FCoE received frame for net device %s missing FCoE " "port\n", netdev->name ); rc = -ENOTCONN; @@ -448,9 +427,6 @@ static void fcoe_close ( struct fcoe_port *fcoe, int rc ) { stop_timer ( &fcoe->timer ); intf_shutdown ( &fcoe->transport, rc ); - netdev_put ( fcoe->netdev ); - list_del ( &fcoe->list ); - ref_put ( &fcoe->refcnt ); } /** @@ -947,7 +923,8 @@ static int fcoe_fip_rx ( struct io_buffer *iobuf, int rc; /* Identify FCoE port */ - if ( ( fcoe = fcoe_demux ( netdev ) ) == NULL ) { + fcoe = netdev_priv ( netdev, &fcoe_driver ); + if ( ! fcoe->netdev ) { DBG ( "FCoE received FIP frame for net device %s missing FCoE " "port\n", netdev->name ); rc = -ENOTCONN; @@ -1113,29 +1090,21 @@ static void fcoe_expired ( struct retry_timer *timer, int over __unused ) { * @v priv Private data * @ret rc Return status code */ -static int fcoe_probe ( struct net_device *netdev, void *priv __unused ) { +static int fcoe_probe ( struct net_device *netdev, void *priv ) { struct ll_protocol *ll_protocol = netdev->ll_protocol; - struct fcoe_port *fcoe; - int rc; + struct fcoe_port *fcoe = priv; /* Sanity check */ if ( ll_protocol->ll_proto != htons ( ARPHRD_ETHER ) ) { /* Not an error; simply skip this net device */ DBG ( "FCoE skipping non-Ethernet device %s\n", netdev->name ); - rc = 0; - goto err_non_ethernet; + return 0; } - /* Allocate and initialise structure */ - fcoe = zalloc ( sizeof ( *fcoe ) ); - if ( ! fcoe ) { - rc = -ENOMEM; - goto err_zalloc; - } - ref_init ( &fcoe->refcnt, NULL ); - intf_init ( &fcoe->transport, &fcoe_transport_desc, &fcoe->refcnt ); - timer_init ( &fcoe->timer, fcoe_expired, &fcoe->refcnt ); - fcoe->netdev = netdev_get ( netdev ); + /* Initialise structure */ + intf_init ( &fcoe->transport, &fcoe_transport_desc, &netdev->refcnt ); + timer_init ( &fcoe->timer, fcoe_expired, &netdev->refcnt ); + fcoe->netdev = netdev; /* Construct node and port names */ fcoe->node_wwn.fcoe.authority = htons ( FCOE_AUTHORITY_IEEE ); @@ -1149,14 +1118,7 @@ static int fcoe_probe ( struct net_device *netdev, void *priv __unused ) { fc_ntoa ( &fcoe->node_wwn.fc ) ); DBGC ( fcoe, " port %s\n", fc_ntoa ( &fcoe->port_wwn.fc ) ); - /* Transfer reference to port list */ - list_add ( &fcoe->list, &fcoe_ports ); return 0; - - netdev_put ( fcoe->netdev ); - err_zalloc: - err_non_ethernet: - return rc; } /** @@ -1165,15 +1127,12 @@ static int fcoe_probe ( struct net_device *netdev, void *priv __unused ) { * @v netdev Network device * @v priv Private data */ -static void fcoe_notify ( struct net_device *netdev, void *priv __unused ) { - struct fcoe_port *fcoe; +static void fcoe_notify ( struct net_device *netdev, void *priv ) { + struct fcoe_port *fcoe = priv; - /* Sanity check */ - if ( ( fcoe = fcoe_demux ( netdev ) ) == NULL ) { - DBG ( "FCoE notification for net device %s missing FCoE " - "port\n", netdev->name ); + /* Skip non-FCoE net devices */ + if ( ! fcoe->netdev ) return; - } /* Reset the FCoE link if necessary */ if ( ! ( netdev_is_open ( netdev ) && @@ -1189,15 +1148,12 @@ static void fcoe_notify ( struct net_device *netdev, void *priv __unused ) { * @v netdev Network device * @v priv Private data */ -static void fcoe_remove ( struct net_device *netdev, void *priv __unused ) { - struct fcoe_port *fcoe; +static void fcoe_remove ( struct net_device *netdev __unused, void *priv ) { + struct fcoe_port *fcoe = priv; - /* Sanity check */ - if ( ( fcoe = fcoe_demux ( netdev ) ) == NULL ) { - DBG ( "FCoE removal of net device %s missing FCoE port\n", - netdev->name ); + /* Skip non-FCoE net devices */ + if ( ! fcoe->netdev ) return; - } /* Close FCoE device */ fcoe_close ( fcoe, 0 ); @@ -1206,6 +1162,7 @@ static void fcoe_remove ( struct net_device *netdev, void *priv __unused ) { /** FCoE driver */ struct net_driver fcoe_driver __net_driver = { .name = "FCoE", + .priv_len = sizeof ( struct fcoe_port ), .probe = fcoe_probe, .notify = fcoe_notify, .remove = fcoe_remove, -- cgit v1.2.3-55-g7522 From 56cc61a168820c7cbbe23418388129ec11699a8c Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Fri, 15 Sep 2023 16:10:07 +0100 Subject: [eap] Define a supplicant model for EAP and EAPoL Extend the EAP model to include a record of whether or not EAP authentication has completed (successfully or otherwise), and to provide a method for transmitting EAP responses. Signed-off-by: Michael Brown --- src/include/ipxe/eap.h | 21 +++++++- src/include/ipxe/eapol.h | 13 +++-- src/net/80211/wpa.c | 11 +++-- src/net/eap.c | 43 +++++++++++------ src/net/eapol.c | 123 +++++++++++++++++++++++++++++++++++++++++++++-- 5 files changed, 184 insertions(+), 27 deletions(-) (limited to 'src/net') diff --git a/src/include/ipxe/eap.h b/src/include/ipxe/eap.h index 6fe70189b..e5f606553 100644 --- a/src/include/ipxe/eap.h +++ b/src/include/ipxe/eap.h @@ -64,6 +64,25 @@ union eap_packet { */ #define EAP_BLOCK_TIMEOUT ( 45 * TICKS_PER_SEC ) -extern int eap_rx ( struct net_device *netdev, const void *data, size_t len ); +/** An EAP supplicant */ +struct eap_supplicant { + /** Network device */ + struct net_device *netdev; + /** Authentication outcome is final */ + int done; + /** + * Transmit EAP response + * + * @v supplicant EAP supplicant + * @v data Response data + * @v len Length of response data + * @ret rc Return status code + */ + int ( * tx ) ( struct eap_supplicant *supplicant, + const void *data, size_t len ); +}; + +extern int eap_rx ( struct eap_supplicant *supplicant, + 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 952d6c752..f6009a2ff 100644 --- a/src/include/ipxe/eapol.h +++ b/src/include/ipxe/eapol.h @@ -12,6 +12,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include #include +#include /** EAPoL header */ struct eapol_header { @@ -32,6 +33,12 @@ struct eapol_header { /** EAPoL key */ #define EAPOL_TYPE_KEY 5 +/** An EAPoL supplicant */ +struct eapol_supplicant { + /** EAP supplicant */ + struct eap_supplicant eap; +}; + /** An EAPoL handler */ struct eapol_handler { /** Type */ @@ -39,15 +46,15 @@ struct eapol_handler { /** * Process received packet * + * @v supplicant EAPoL supplicant * @v iobuf I/O buffer - * @v netdev Network device * @v ll_source Link-layer source address * @ret rc Return status code * * This method takes ownership of the I/O buffer. */ - int ( * rx ) ( struct io_buffer *iobuf, struct net_device *netdev, - const void *ll_source ); + int ( * rx ) ( struct eapol_supplicant *supplicant, + struct io_buffer *iobuf, const void *ll_source ); }; /** EAPoL handler table */ diff --git a/src/net/80211/wpa.c b/src/net/80211/wpa.c index 1484d0e80..17c11b8ed 100644 --- a/src/net/80211/wpa.c +++ b/src/net/80211/wpa.c @@ -761,13 +761,14 @@ static int wpa_handle_1_of_2 ( struct wpa_common_ctx *ctx, /** * Handle receipt of EAPOL-Key frame for WPA * - * @v iob I/O buffer - * @v netdev Network device - * @v ll_source Source link-layer address + * @v supplicant EAPoL supplicant + * @v iob I/O buffer + * @v ll_source Source link-layer address */ -static int eapol_key_rx ( struct io_buffer *iob, struct net_device *netdev, - const void *ll_source ) +static int eapol_key_rx ( struct eapol_supplicant *supplicant, + struct io_buffer *iob, const void *ll_source ) { + struct net_device *netdev = supplicant->eap.netdev; struct net80211_device *dev = net80211_get ( netdev ); struct eapol_header *eapol; struct eapol_key_pkt *pkt; diff --git a/src/net/eap.c b/src/net/eap.c index 8d1d540fb..beaeb61d2 100644 --- a/src/net/eap.c +++ b/src/net/eap.c @@ -36,10 +36,11 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** * Handle EAP Request-Identity * - * @v netdev Network device + * @v supplicant EAP supplicant * @ret rc Return status code */ -static int eap_rx_request_identity ( struct net_device *netdev ) { +static int eap_rx_request_identity ( struct eap_supplicant *supplicant ) { + struct net_device *netdev = supplicant->netdev; /* Treat Request-Identity as blocking the link */ DBGC ( netdev, "EAP %s Request-Identity blocking link\n", @@ -52,13 +53,14 @@ static int eap_rx_request_identity ( struct net_device *netdev ) { /** * Handle EAP Request * - * @v netdev Network device + * @v supplicant EAP supplicant * @v req EAP request * @v len Length of EAP request * @ret rc Return status code */ -static int eap_rx_request ( struct net_device *netdev, +static int eap_rx_request ( struct eap_supplicant *supplicant, const struct eap_request *req, size_t len ) { + struct net_device *netdev = supplicant->netdev; /* Sanity check */ if ( len < sizeof ( *req ) ) { @@ -67,10 +69,13 @@ static int eap_rx_request ( struct net_device *netdev, return -EINVAL; } + /* Mark authentication as incomplete */ + supplicant->done = 0; + /* Handle according to type */ switch ( req->type ) { case EAP_TYPE_IDENTITY: - return eap_rx_request_identity ( netdev ); + return eap_rx_request_identity ( supplicant ); default: DBGC ( netdev, "EAP %s requested type %d unknown:\n", netdev->name, req->type ); @@ -82,10 +87,14 @@ static int eap_rx_request ( struct net_device *netdev, /** * Handle EAP Success * - * @v netdev Network device + * @v supplicant EAP supplicant * @ret rc Return status code */ -static int eap_rx_success ( struct net_device *netdev ) { +static int eap_rx_success ( struct eap_supplicant *supplicant ) { + struct net_device *netdev = supplicant->netdev; + + /* Mark authentication as complete */ + supplicant->done = 1; /* Mark link as unblocked */ DBGC ( netdev, "EAP %s Success\n", netdev->name ); @@ -97,10 +106,14 @@ static int eap_rx_success ( struct net_device *netdev ) { /** * Handle EAP Failure * - * @v netdev Network device + * @v supplicant EAP supplicant * @ret rc Return status code */ -static int eap_rx_failure ( struct net_device *netdev ) { +static int eap_rx_failure ( struct eap_supplicant *supplicant ) { + struct net_device *netdev = supplicant->netdev; + + /* Mark authentication as complete */ + supplicant->done = 1; /* Record error */ DBGC ( netdev, "EAP %s Failure\n", netdev->name ); @@ -110,12 +123,14 @@ static int eap_rx_failure ( struct net_device *netdev ) { /** * Handle EAP packet * - * @v netdev Network device + * @v supplicant EAP supplicant * @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 ) { +int eap_rx ( struct eap_supplicant *supplicant, const void *data, + size_t len ) { + struct net_device *netdev = supplicant->netdev; const union eap_packet *eap = data; /* Sanity check */ @@ -128,11 +143,11 @@ int eap_rx ( struct net_device *netdev, const void *data, size_t len ) { /* Handle according to code */ switch ( eap->hdr.code ) { case EAP_CODE_REQUEST: - return eap_rx_request ( netdev, &eap->req, len ); + return eap_rx_request ( supplicant, &eap->req, len ); case EAP_CODE_SUCCESS: - return eap_rx_success ( netdev ); + return eap_rx_success ( supplicant ); case EAP_CODE_FAILURE: - return eap_rx_failure ( netdev ); + return eap_rx_failure ( supplicant ); default: DBGC ( netdev, "EAP %s unsupported code %d\n", netdev->name, eap->hdr.code ); diff --git a/src/net/eapol.c b/src/net/eapol.c index 3578f0e37..172037ce1 100644 --- a/src/net/eapol.c +++ b/src/net/eapol.c @@ -28,7 +28,9 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include #include +#include #include +#include #include #include @@ -38,6 +40,13 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); * */ +struct net_driver eapol_driver __net_driver; + +/** EAPoL destination MAC address */ +static const uint8_t eapol_mac[ETH_ALEN] = { + 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03 +}; + /** * Process EAPoL packet * @@ -51,12 +60,25 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); 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 ) { + struct eapol_supplicant *supplicant; struct eapol_header *eapol; struct eapol_handler *handler; size_t remaining; size_t len; int rc; + /* Find matching supplicant */ + supplicant = netdev_priv ( netdev, &eapol_driver ); + + /* Ignore non-EAPoL devices */ + if ( ! supplicant->eap.netdev ) { + DBGC ( netdev, "EAPOL %s is not an EAPoL device\n", + netdev->name ); + DBGC_HDA ( netdev, 0, iobuf->data, iob_len ( iobuf ) ); + rc = -ENOTTY; + goto drop; + } + /* Sanity checks */ if ( iob_len ( iobuf ) < sizeof ( *eapol ) ) { DBGC ( netdev, "EAPOL %s underlength header:\n", @@ -83,7 +105,7 @@ static int eapol_rx ( struct io_buffer *iobuf, struct net_device *netdev, /* Handle according to type */ for_each_table_entry ( handler, EAPOL_HANDLERS ) { if ( handler->type == eapol->type ) { - return handler->rx ( iob_disown ( iobuf ) , netdev, + return handler->rx ( supplicant, iob_disown ( iobuf ), ll_source ); } } @@ -107,12 +129,14 @@ struct net_protocol eapol_protocol __net_protocol = { /** * Process EAPoL-encapsulated EAP packet * - * @v netdev Network device + * @v supplicant EAPoL supplicant * @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, +static int eapol_eap_rx ( struct eapol_supplicant *supplicant, + struct io_buffer *iobuf, const void *ll_source __unused ) { + struct net_device *netdev = supplicant->eap.netdev; struct eapol_header *eapol; int rc; @@ -123,7 +147,8 @@ static int eapol_eap_rx ( struct io_buffer *iobuf, struct net_device *netdev, eapol = iob_pull ( iobuf, sizeof ( *eapol ) ); /* Process EAP packet */ - if ( ( rc = eap_rx ( netdev, iobuf->data, iob_len ( iobuf ) ) ) != 0 ) { + if ( ( rc = eap_rx ( &supplicant->eap, iobuf->data, + iob_len ( iobuf ) ) ) != 0 ) { DBGC ( netdev, "EAPOL %s v%d EAP failed: %s\n", netdev->name, eapol->version, strerror ( rc ) ); goto drop; @@ -139,3 +164,93 @@ struct eapol_handler eapol_eap __eapol_handler = { .type = EAPOL_TYPE_EAP, .rx = eapol_eap_rx, }; + +/** + * Transmit EAPoL packet + * + * @v supplicant EAPoL supplicant + * @v type Packet type + * @v data Packet body + * @v len Length of packet body + * @ret rc Return status code + */ +static int eapol_tx ( struct eapol_supplicant *supplicant, unsigned int type, + const void *data, size_t len ) { + struct net_device *netdev = supplicant->eap.netdev; + struct io_buffer *iobuf; + struct eapol_header *eapol; + int rc; + + /* Allocate I/O buffer */ + iobuf = alloc_iob ( MAX_LL_HEADER_LEN + sizeof ( *eapol ) + len ); + if ( ! iobuf ) + return -ENOMEM; + iob_reserve ( iobuf, MAX_LL_HEADER_LEN ); + + /* Construct EAPoL header */ + eapol = iob_put ( iobuf, sizeof ( *eapol ) ); + eapol->version = EAPOL_VERSION_2001; + eapol->type = type; + eapol->len = htons ( len ); + + /* Append packet body */ + memcpy ( iob_put ( iobuf, len ), data, len ); + + /* Transmit packet */ + if ( ( rc = net_tx ( iob_disown ( iobuf ), netdev, &eapol_protocol, + &eapol_mac, netdev->ll_addr ) ) != 0 ) { + DBGC ( netdev, "EAPOL %s could not transmit type %d: %s\n", + netdev->name, type, strerror ( rc ) ); + DBGC_HDA ( netdev, 0, data, len ); + return rc; + } + + return 0; +} + +/** + * Transmit EAPoL-encapsulated EAP packet + * + * @v supplicant EAPoL supplicant + * @v ll_source Link-layer source address + * @ret rc Return status code + */ +static int eapol_eap_tx ( struct eap_supplicant *eap, const void *data, + size_t len ) { + struct eapol_supplicant *supplicant = + container_of ( eap, struct eapol_supplicant, eap ); + + /* Transmit encapsulated packet */ + return eapol_tx ( supplicant, EAPOL_TYPE_EAP, data, len ); +} + +/** + * Create EAPoL supplicant + * + * @v netdev Network device + * @v priv Private data + * @ret rc Return status code + */ +static int eapol_probe ( struct net_device *netdev, void *priv ) { + struct eapol_supplicant *supplicant = priv; + struct ll_protocol *ll_protocol = netdev->ll_protocol; + + /* Ignore non-EAPoL devices */ + if ( ll_protocol->ll_proto != htons ( ARPHRD_ETHER ) ) + return 0; + if ( vlan_tag ( netdev ) ) + return 0; + + /* Initialise structure */ + supplicant->eap.netdev = netdev; + supplicant->eap.tx = eapol_eap_tx; + + return 0; +} + +/** EAPoL driver */ +struct net_driver eapol_driver __net_driver = { + .name = "EAPoL", + .priv_len = sizeof ( struct eapol_supplicant ), + .probe = eapol_probe, +}; -- cgit v1.2.3-55-g7522 From 8b14652e506d99499cfbeaed0df07d6a83ec029e Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Fri, 15 Sep 2023 16:14:59 +0100 Subject: [eapol] Send EAPoL-Start packets to trigger EAP authentication We have no way to force a link-layer restart in iPXE, and therefore no way to explicitly trigger a restart of EAP authentication. If an iPXE script has performed some action that requires such a restart (e.g. registering a device such that the port VLAN assignment will be changed), then the only means currently available to effect the restart is to reboot the whole system. If iPXE is taking over a physical link already used by a preceding bootloader, then even a reboot may not work. In the EAP model, the supplicant is a pure responder and never initiates transmissions. EAPoL extends this to include an EAPoL-Start packet type that may be sent by the supplicant to (re)trigger EAP. Add support for sending EAPoL-Start packets at two-second intervals on links that are open and have reached physical link-up, but for which EAP has not yet completed. This allows "ifclose ; ifopen" to be used to restart the EAP process. Signed-off-by: Michael Brown --- src/include/ipxe/eapol.h | 8 ++++++ src/net/eapol.c | 73 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 81 insertions(+) (limited to 'src/net') diff --git a/src/include/ipxe/eapol.h b/src/include/ipxe/eapol.h index f6009a2ff..d4ea39208 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 172037ce1..1b843e896 100644 --- a/src/net/eapol.c +++ b/src/net/eapol.c @@ -31,6 +31,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include #include #include +#include #include #include @@ -47,6 +48,37 @@ static const uint8_t eapol_mac[ETH_ALEN] = { 0x01, 0x80, 0xc2, 0x00, 0x00, 0x03 }; +/** + * 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 * @@ -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; @@ -224,6 +259,25 @@ static int eapol_eap_tx ( struct eap_supplicant *eap, const void *data, return eapol_tx ( supplicant, EAPOL_TYPE_EAP, data, len ); } +/** + * (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 * @@ -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, }; -- cgit v1.2.3-55-g7522