Compare commits

..

13 Commits

Author SHA1 Message Date
2dcef4b7a1 [efi] Create VLAN autoboot device automatically
When chainloading iPXE from an EFI VLAN device, configure the
corresponding iPXE VLAN device to be created automatically.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2023-01-15 22:42:30 +00:00
f07630c74f [vlan] Support automatic VLAN device creation
Add the ability to automatically create a VLAN device for a specified
trunk device link-layer address and VLAN tag.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2023-01-15 22:35:44 +00:00
5a2fa6040e [autoboot] Include VLAN tag in filter for identifying autoboot device
When chainloading iPXE from a VLAN device, the MAC address of the
loaded image's device handle will match the MAC address of the trunk
device created by iPXE, and the autoboot process will then erroneously
consider the trunk device to be an autoboot device.

Fix by recording the VLAN tag along with the MAC address, and treating
the VLAN tag as part of the filter used to match the MAC address
against candidate network devices.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2023-01-15 21:36:08 +00:00
c4c03e5be8 [netdevice] Allow duplicate MAC addresses
Many laptops now include the ability to specify a "system-specific MAC
address" (also known as "pass-through MAC"), which is supposed to be
used for both the onboard NIC and for any attached docking station or
other USB NIC.  This is intended to simplify interoperability with
software or hardware that relies on a MAC address to recognise an
individual machine: for example, a deployment server may associate the
MAC address with a particular operating system image to be deployed.
This therefore creates legitimate situations in which duplicate MAC
addresses may exist within the same system.

As described in commit 98d09a1 ("[netdevice] Avoid registering
duplicate network devices"), the Xen netfront driver relies on the
rejection of duplicate MAC addresses in order to inhibit registration
of the emulated PCI devices that a Xen PV-HVM guest will create to
shadow each of the paravirtual network devices.

Move the code that rejects duplicate MAC addresses from the network
device core to the Xen netfront driver, to allow for the existence of
duplicate MAC addresses in non-Xen setups.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2023-01-15 00:42:52 +00:00
47af48012e [netdevice] Separate concept of scope ID from network device name index
The network device index currently serves two purposes: acting as a
sequential index for network device names ("net0", "net1", etc), and
acting as an opaque unique integer identifier used in socket address
scope IDs.

There is no particular need for these usages to be linked, and it can
lead to situations in which devices are named unexpectedly.  For
example: if a system has two network devices "net0" and "net1", a VLAN
is created as "net1-42", and then a USB NIC is connected, then the USB
NIC will be named "net3" rather than the expected "net2" since the
VLAN device "net1-42" will have consumed an index.

Separate the usages: rename the "index" field to "scope_id" (matching
its one and only use case), and assign the name without reference to
the scope ID by finding the first unused name.  For consistency,
assign the scope ID by similarly finding the first unused scope ID.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2023-01-14 00:09:20 +00:00
ab19546386 [efi] Disable receive filters to work around buggy UNDI drivers
Some UNDI drivers (such as the AMI UsbNetworkPkg currently in the
process of being upstreamed into EDK2) have a bug that will prevent
any packets from being received unless at least one attempt has been
made to disable some receive filters.

Work around these buggy drivers by attempting to disable receive
filters before enabling them.  Ignore any errors, since we genuinely
do not care whether or not the disabling succeeds.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2023-01-11 00:18:18 +00:00
7147532c3f [cachedhcp] Retain cached DHCPACK after startup if not already consumed
We currently free an unclaimed cached DHCPACK immediately after
startup, in order to free up memory.  This prevents the cached DHCPACK
from being applied to a device that is created after startup, such as
a VLAN device created via the "vcreate" command.

Retain any unclaimed DHCPACK after startup to allow it to be matched
against (and applied to) any device that gets created at runtime.
Free the DHCPACK during shutdown if it still remains unclaimed, in
order to exit with memory cleanly freed.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2022-12-22 15:12:34 +00:00
60b5532cfc [cachedhcp] Include VLAN tag in filter for applying cached DHCPACK
When chainloading iPXE from a VLAN device, the MAC address within the
cached DHCPACK will match the MAC address of the trunk device created
by iPXE, and the cached DHCPACK will then end up being erroneously
applied to the trunk device.  This tends to break outbound IPv4
routing, since both the trunk and VLAN devices will have the same
assigned IPv4 address.

Fix by recording the VLAN tag along with the cached DHCPACK, and
treating the VLAN tag as part of the filter used to match the cached
DHCPACK against candidate network devices.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2022-12-22 14:59:29 +00:00
b9571ca12e [efi] Add efi_path_vlan() utility function
EFI provides no API for determining the VLAN tag (if any) for a
specified device handle.  There is the EFI_VLAN_CONFIG_PROTOCOL, but
that exists only on the trunk device handle (not on the VLAN device
handle), and provides no way to match VLAN tags against the trunk
device's child device handles.

The EDK2 codebase seems to rely solely on the device path to determine
the VLAN tag for a specified device handle: both NetLibGetVlanId() and
BmGetNetworkDescription() will parse the device path to search for a
VLAN_DEVICE_PATH component.

Add efi_path_vlan() which uses the same device path parsing logic to
determine the VLAN tag.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2022-12-22 14:27:56 +00:00
099e4d39b3 [efi] Expose efi_path_next() utility function
Provide a single central implementation of the logic for stepping
through elements of an EFI device path.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2022-12-22 13:34:28 +00:00
0f3ace92c6 [efi] Allow passing a NULL device path to path utility functions
Signed-off-by: Michael Brown <mcb30@ipxe.org>
2022-12-22 13:30:02 +00:00
d879c8e4d9 [efi] Provide VLAN configuration protocol
UEFI implements VLAN support within the Managed Network Protocol (MNP)
driver, which may create child VLAN devices automatically based on
stored UEFI variables.  These child devices do not themselves provide
a raw-packet interface via EFI_SIMPLE_NETWORK_PROTOCOL, and may be
consumed only via the EFI_MANAGED_NETWORK_PROTOCOL interface.

The device paths constructed for these child devices may conflict with
those for the EFI_SIMPLE_NETWORK_PROTOCOL instances that iPXE attempts
to install for its own VLAN devices.  The upshot is that creating an
iPXE VLAN device (e.g. via the "vcreate" command) will fail if the
UEFI Managed Network Protocol has already created a device for the
same VLAN tag.

Fix by providing our own EFI_VLAN_CONFIG_PROTOCOL instance on the same
device handle as EFI_SIMPLE_NETWORK_PROTOCOL.  This causes the MNP
driver to treat iPXE's device as supporting hardware VLAN offload, and
it will therefore not attempt to install its own instance of the
protocol.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
2022-12-14 11:51:52 +00:00
5e62b4bc6c [vlan] Allow external code to identify VLAN priority as well as tag
Signed-off-by: Michael Brown <mcb30@ipxe.org>
2022-12-14 11:05:37 +00:00
32 changed files with 604 additions and 137 deletions

View File

@ -59,7 +59,7 @@ static void cachedhcp_init ( void ) {
}
/* Record cached DHCPACK */
if ( ( rc = cachedhcp_record ( &cached_dhcpack,
if ( ( rc = cachedhcp_record ( &cached_dhcpack, 0,
phys_to_user ( cached_dhcpack_phys ),
sizeof ( BOOTPLAYER_t ) ) ) != 0 ) {
DBGC ( colour, "CACHEDHCP could not record DHCPACK: %s\n",

View File

@ -29,6 +29,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <ipxe/dhcppkt.h>
#include <ipxe/init.h>
#include <ipxe/netdevice.h>
#include <ipxe/vlan.h>
#include <ipxe/cachedhcp.h>
/** @file
@ -43,6 +44,8 @@ struct cached_dhcp_packet {
const char *name;
/** DHCP packet (if any) */
struct dhcp_packet *dhcppkt;
/** VLAN tag (if applicable) */
unsigned int vlan;
};
/** Cached DHCPACK */
@ -136,15 +139,26 @@ static int cachedhcp_apply ( struct cached_dhcp_packet *cache,
* matches this network device.
*/
if ( memcmp ( ll_addr, chaddr, ll_addr_len ) != 0 ) {
DBGC ( colour, "CACHEDHCP %s does not match %s\n",
cache->name, netdev->name );
DBGC ( colour, "CACHEDHCP %s %s does not match %s\n",
cache->name, ll_protocol->ntoa ( chaddr ),
netdev->name );
return 0;
}
/* Do nothing unless cached packet's VLAN tag matches
* this network device.
*/
if ( vlan_tag ( netdev ) != cache->vlan ) {
DBGC ( colour, "CACHEDHCP %s VLAN %d does not match "
"%s\n", cache->name, cache->vlan,
netdev->name );
return 0;
}
DBGC ( colour, "CACHEDHCP %s is for %s\n",
cache->name, netdev->name );
/* Use network device's settings block */
settings = netdev_settings ( netdev );
DBGC ( colour, "CACHEDHCP %s is for %s\n",
cache->name, netdev->name );
}
/* Register settings */
@ -165,12 +179,13 @@ static int cachedhcp_apply ( struct cached_dhcp_packet *cache,
* Record cached DHCP packet
*
* @v cache Cached DHCP packet
* @v vlan VLAN tag, if any
* @v data DHCPACK packet buffer
* @v max_len Maximum possible length
* @ret rc Return status code
*/
int cachedhcp_record ( struct cached_dhcp_packet *cache, userptr_t data,
size_t max_len ) {
int cachedhcp_record ( struct cached_dhcp_packet *cache, unsigned int vlan,
userptr_t data, size_t max_len ) {
struct dhcp_packet *dhcppkt;
struct dhcp_packet *tmp;
struct dhcphdr *dhcphdr;
@ -225,36 +240,55 @@ int cachedhcp_record ( struct cached_dhcp_packet *cache, userptr_t data,
DBGC ( colour, "CACHEDHCP %s at %#08lx+%#zx/%#zx\n", cache->name,
user_to_phys ( data, 0 ), len, max_len );
cache->dhcppkt = dhcppkt;
cache->vlan = vlan;
return 0;
}
/**
* Cached DHCPACK startup function
* Cached DHCP packet startup function
*
*/
static void cachedhcp_startup ( void ) {
/* Apply cached ProxyDHCPOFFER, if any */
cachedhcp_apply ( &cached_proxydhcp, NULL );
cachedhcp_free ( &cached_proxydhcp );
/* Apply cached PXEBSACK, if any */
cachedhcp_apply ( &cached_pxebs, NULL );
cachedhcp_free ( &cached_pxebs );
/* Free any remaining cached packets */
/* Report unclaimed DHCPACK, if any. Do not free yet, since
* it may still be claimed by a dynamically created device
* such as a VLAN device.
*/
if ( cached_dhcpack.dhcppkt ) {
DBGC ( colour, "CACHEDHCP %s unclaimed\n",
cached_dhcpack.name );
}
}
/**
* Cached DHCP packet shutdown function
*
* @v booting System is shutting down for OS boot
*/
static void cachedhcp_shutdown ( int booting __unused ) {
/* Free cached DHCPACK, if any */
if ( cached_dhcpack.dhcppkt ) {
DBGC ( colour, "CACHEDHCP %s never claimed\n",
cached_dhcpack.name );
}
cachedhcp_free ( &cached_dhcpack );
cachedhcp_free ( &cached_proxydhcp );
cachedhcp_free ( &cached_pxebs );
}
/** Cached DHCPACK startup function */
struct startup_fn cachedhcp_startup_fn __startup_fn ( STARTUP_LATE ) = {
.name = "cachedhcp",
.startup = cachedhcp_startup,
.shutdown = cachedhcp_shutdown,
};
/**

View File

@ -121,10 +121,9 @@ int ecm_fetch_mac ( struct usb_function *func,
}
/* Apply system-specific MAC address as current link-layer
* address, if present and not already used.
* address, if present.
*/
if ( ( ( rc = acpi_mac ( amac ) ) == 0 ) &&
! find_netdev_by_ll_addr ( &ethernet_protocol, amac ) ) {
if ( ( rc = acpi_mac ( amac ) ) == 0 ) {
memcpy ( netdev->ll_addr, amac, ETH_ALEN );
DBGC ( usb, "USB %s using system-specific MAC %s\n",
func->name, eth_ntoa ( netdev->ll_addr ) );

View File

@ -921,18 +921,17 @@ static int nii_set_station_address ( struct nii_nic *nii,
* Set receive filters
*
* @v nii NII NIC
* @v flags Flags
* @ret rc Return status code
*/
static int nii_set_rx_filters ( struct nii_nic *nii ) {
static int nii_set_rx_filters ( struct nii_nic *nii, unsigned int flags ) {
uint32_t implementation = nii->undi->Implementation;
unsigned int flags;
unsigned int op;
int stat;
int rc;
/* Construct receive filter set */
flags = ( PXE_OPFLAGS_RECEIVE_FILTER_ENABLE |
PXE_OPFLAGS_RECEIVE_FILTER_UNICAST );
flags |= PXE_OPFLAGS_RECEIVE_FILTER_UNICAST;
if ( implementation & PXE_ROMID_IMP_BROADCAST_RX_SUPPORTED )
flags |= PXE_OPFLAGS_RECEIVE_FILTER_BROADCAST;
if ( implementation & PXE_ROMID_IMP_PROMISCUOUS_RX_SUPPORTED )
@ -944,14 +943,40 @@ static int nii_set_rx_filters ( struct nii_nic *nii ) {
op = NII_OP ( PXE_OPCODE_RECEIVE_FILTERS, flags );
if ( ( stat = nii_issue ( nii, op ) ) < 0 ) {
rc = -EIO_STAT ( stat );
DBGC ( nii, "NII %s could not set receive filters %#04x: %s\n",
nii->dev.name, flags, strerror ( rc ) );
DBGC ( nii, "NII %s could not %s%sable receive filters "
"%#04x: %s\n", nii->dev.name,
( ( flags & PXE_OPFLAGS_RECEIVE_FILTER_ENABLE ) ?
"en" : "" ),
( ( flags & PXE_OPFLAGS_RECEIVE_FILTER_DISABLE ) ?
"dis" : "" ), flags, strerror ( rc ) );
return rc;
}
return 0;
}
/**
* Enable receive filters
*
* @v nii NII NIC
* @ret rc Return status code
*/
static int nii_enable_rx_filters ( struct nii_nic *nii ) {
return nii_set_rx_filters ( nii, PXE_OPFLAGS_RECEIVE_FILTER_ENABLE );
}
/**
* Disable receive filters
*
* @v nii NII NIC
* @ret rc Return status code
*/
static int nii_disable_rx_filters ( struct nii_nic *nii ) {
return nii_set_rx_filters ( nii, PXE_OPFLAGS_RECEIVE_FILTER_DISABLE );
}
/**
* Transmit packet
*
@ -1175,13 +1200,25 @@ static int nii_open ( struct net_device *netdev ) {
/* Treat as non-fatal */
}
/* Set receive filters */
if ( ( rc = nii_set_rx_filters ( nii ) ) != 0 )
goto err_set_rx_filters;
/* Disable receive filters
*
* We have no reason to disable receive filters here (or
* anywhere), but some NII drivers have a bug which prevents
* packets from being received unless we attempt to disable
* the receive filters.
*
* Ignore any failures, since we genuinely don't care if the
* NII driver cannot disable the filters.
*/
nii_disable_rx_filters ( nii );
/* Enable receive filters */
if ( ( rc = nii_enable_rx_filters ( nii ) ) != 0 )
goto err_enable_rx_filters;
return 0;
err_set_rx_filters:
err_enable_rx_filters:
nii_shutdown ( nii );
err_initialise:
return rc;

View File

@ -59,6 +59,9 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
EUNIQ ( EINFO_EIO, ( -(status) & 0x1f ), \
EIO_NETIF_RSP_ERROR, EIO_NETIF_RSP_DROPPED )
/** List of netfront devices */
static LIST_HEAD ( netfront_devices );
/******************************************************************************
*
* XenStore interface
@ -952,6 +955,7 @@ static int netfront_probe ( struct xen_device *xendev ) {
netdev->dev = &xendev->dev;
netfront = netdev->priv;
netfront->xendev = xendev;
netfront->netdev = netdev;
INIT_LIST_HEAD ( &netfront->rx_partial );
DBGC ( netfront, "NETFRONT %s backend=\"%s\" in domain %ld\n",
xendev->key, xendev->backend, xendev->backend_id );
@ -991,9 +995,13 @@ static int netfront_probe ( struct xen_device *xendev ) {
/* Set initial link state */
netdev_link_down ( netdev );
/* Add to list of netfront devices */
list_add_tail ( &netfront->list, &netfront_devices );
xen_set_drvdata ( xendev, netdev );
return 0;
list_del ( &netfront->list );
unregister_netdev ( netdev );
err_register_netdev:
err_read_mac:
@ -1015,6 +1023,9 @@ static void netfront_remove ( struct xen_device *xendev ) {
struct netfront_nic *netfront = netdev->priv;
struct xen_hypervisor *xen = xendev->xen;
/* Remove from list of netfront devices */
list_del ( &netfront->list );
/* Unregister network device */
unregister_netdev ( netdev );
@ -1033,3 +1044,41 @@ struct xen_driver netfront_driver __xen_driver = {
.probe = netfront_probe,
.remove = netfront_remove,
};
/******************************************************************************
*
* Emulated PCI device inhibitor
*
******************************************************************************
*/
/**
* Inhibit emulated PCI devices
*
* @v netdev Network device
* @ret rc Return status code
*/
static int netfront_net_probe ( struct net_device *netdev ) {
struct netfront_nic *netfront;
/* Inhibit emulated PCI devices matching an existing netfront device */
list_for_each_entry ( netfront, &netfront_devices, list ) {
if ( ( netdev->dev != netfront->netdev->dev ) &&
( netdev->ll_protocol->ll_addr_len == ETH_ALEN ) &&
( memcmp ( netdev->hw_addr, netfront->netdev->hw_addr,
ETH_ALEN ) == 0 ) ) {
DBGC ( netfront, "NETFRONT %s inhibiting emulated %s "
"%s\n", netfront->xendev->key,
netdev->dev->driver_name, netdev->dev->name );
return -EEXIST;
}
}
return 0;
}
/** Emulated PCI device inhibitor driver */
struct net_driver netfront_net_driver __net_driver = {
.name = "netfront",
.probe = netfront_net_probe,
};

View File

@ -159,6 +159,11 @@ struct netfront_nic {
/** Grant references */
grant_ref_t refs[NETFRONT_REF_COUNT];
/** Network device */
struct net_device *netdev;
/** List of netfront NICs */
struct list_head list;
/** Transmit ring */
struct netfront_ring tx;
/** Transmit front ring */

View File

@ -18,7 +18,8 @@ extern struct cached_dhcp_packet cached_dhcpack;
extern struct cached_dhcp_packet cached_proxydhcp;
extern struct cached_dhcp_packet cached_pxebs;
extern int cachedhcp_record ( struct cached_dhcp_packet *cache, userptr_t data,
extern int cachedhcp_record ( struct cached_dhcp_packet *cache,
unsigned int vlan, userptr_t data,
size_t max_len );
#endif /* _IPXE_CACHEDHCP_H */

View File

@ -11,6 +11,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <ipxe/efi/efi.h>
extern int efi_set_autoboot_ll_addr ( EFI_HANDLE device );
extern int efi_set_autoboot_ll_addr ( EFI_HANDLE device,
EFI_DEVICE_PATH_PROTOCOL *path );
#endif /* _IPXE_EFI_AUTOBOOT_H */

View File

@ -11,6 +11,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <ipxe/efi/efi.h>
extern int efi_cachedhcp_record ( EFI_HANDLE device );
extern int efi_cachedhcp_record ( EFI_HANDLE device,
EFI_DEVICE_PATH_PROTOCOL *path );
#endif /* _IPXE_EFI_CACHEDHCP_H */

View File

@ -19,9 +19,11 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <ipxe/efi/Protocol/PxeBaseCode.h>
#include <ipxe/efi/Protocol/SimpleNetwork.h>
#include <ipxe/efi/Protocol/UsbIo.h>
#include <ipxe/efi/Protocol/VlanConfig.h>
extern void efi_nullify_snp ( EFI_SIMPLE_NETWORK_PROTOCOL *snp );
extern void efi_nullify_nii ( EFI_NETWORK_INTERFACE_IDENTIFIER_PROTOCOL *nii );
extern void efi_nullify_vlan ( EFI_VLAN_CONFIG_PROTOCOL *vcfg );
extern void efi_nullify_name2 ( EFI_COMPONENT_NAME2_PROTOCOL *name2 );
extern void efi_nullify_load_file ( EFI_LOAD_FILE_PROTOCOL *load_file );
extern void efi_nullify_hii ( EFI_HII_CONFIG_ACCESS_PROTOCOL *hii );

View File

@ -21,9 +21,12 @@ struct fcp_description;
struct ib_srp_device;
struct usb_function;
extern EFI_DEVICE_PATH_PROTOCOL *
efi_path_next ( EFI_DEVICE_PATH_PROTOCOL *path );
extern EFI_DEVICE_PATH_PROTOCOL *
efi_path_end ( EFI_DEVICE_PATH_PROTOCOL *path );
extern size_t efi_path_len ( EFI_DEVICE_PATH_PROTOCOL *path );
extern unsigned int efi_path_vlan ( EFI_DEVICE_PATH_PROTOCOL *path );
extern EFI_DEVICE_PATH_PROTOCOL * efi_paths ( EFI_DEVICE_PATH_PROTOCOL *first,
... );
extern EFI_DEVICE_PATH_PROTOCOL * efi_netdev_path ( struct net_device *netdev );

View File

@ -19,6 +19,7 @@ FILE_LICENCE ( GPL2_OR_LATER );
#include <ipxe/efi/Protocol/HiiConfigAccess.h>
#include <ipxe/efi/Protocol/HiiDatabase.h>
#include <ipxe/efi/Protocol/LoadFile.h>
#include <ipxe/efi/Protocol/VlanConfig.h>
/** SNP transmit completion ring size */
#define EFI_SNP_NUM_TX 32
@ -51,6 +52,8 @@ struct efi_snp_device {
struct list_head rx;
/** The network interface identifier */
EFI_NETWORK_INTERFACE_IDENTIFIER_PROTOCOL nii;
/** VLAN configuration protocol */
EFI_VLAN_CONFIG_PROTOCOL vcfg;
/** Component name protocol */
EFI_COMPONENT_NAME2_PROTOCOL name2;
/** Load file protocol handle */

View File

@ -356,8 +356,8 @@ struct net_device {
struct list_head list;
/** List of open network devices */
struct list_head open_list;
/** Index of this network device */
unsigned int index;
/** Scope ID */
unsigned int scope_id;
/** Name of this network device */
char name[NETDEV_NAME_LEN];
/** Underlying hardware device */
@ -726,11 +726,9 @@ extern void netdev_close ( struct net_device *netdev );
extern void unregister_netdev ( struct net_device *netdev );
extern void netdev_irq ( struct net_device *netdev, int enable );
extern struct net_device * find_netdev ( const char *name );
extern struct net_device * find_netdev_by_index ( unsigned int index );
extern struct net_device * find_netdev_by_scope_id ( unsigned int scope_id );
extern struct net_device * find_netdev_by_location ( unsigned int bus_type,
unsigned int location );
extern struct net_device *
find_netdev_by_ll_addr ( struct ll_protocol *ll_protocol, const void *ll_addr );
extern struct net_device * last_opened_netdev ( void );
extern int net_tx ( struct io_buffer *iobuf, struct net_device *netdev,
struct net_protocol *net_protocol, const void *ll_dest,

View File

@ -61,11 +61,26 @@ struct vlan_header {
*/
#define VLAN_PRIORITY_IS_VALID( priority ) ( (priority) <= 7 )
extern unsigned int vlan_tag ( struct net_device *netdev );
extern unsigned int vlan_tci ( struct net_device *netdev );
/**
* Get the VLAN tag
*
* @v netdev Network device
* @ret tag VLAN tag, or 0 if device is not a VLAN device
*/
static inline __attribute__ (( always_inline )) unsigned int
vlan_tag ( struct net_device *netdev ) {
return VLAN_TAG ( vlan_tci ( netdev ) );
}
extern struct net_device * vlan_find ( struct net_device *trunk,
unsigned int tag );
extern int vlan_can_be_trunk ( struct net_device *trunk );
extern int vlan_create ( struct net_device *trunk, unsigned int tag,
unsigned int priority );
extern int vlan_destroy ( struct net_device *netdev );
extern void vlan_auto ( const void *ll_addr, unsigned int tag );
extern void vlan_netdev_rx ( struct net_device *netdev, unsigned int tag,
struct io_buffer *iobuf );
extern void vlan_netdev_rx_err ( struct net_device *netdev, unsigned int tag,

View File

@ -28,7 +28,8 @@ enum uriboot_flags {
extern void set_autoboot_busloc ( unsigned int bus_type,
unsigned int location );
extern void set_autoboot_ll_addr ( const void *ll_addr, size_t len );
extern void set_autoboot_ll_addr ( const void *ll_addr, size_t len,
unsigned int vlan );
extern int uriboot ( struct uri *filename, struct uri **root_paths,
unsigned int root_path_count, int drive,

View File

@ -25,7 +25,10 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <string.h>
#include <errno.h>
#include <ipxe/if_ether.h>
#include <ipxe/vlan.h>
#include <ipxe/efi/efi.h>
#include <ipxe/efi/efi_path.h>
#include <ipxe/efi/efi_autoboot.h>
#include <ipxe/efi/Protocol/SimpleNetwork.h>
#include <usr/autoboot.h>
@ -40,9 +43,11 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
* Identify autoboot device
*
* @v device Device handle
* @v path Device path
* @ret rc Return status code
*/
int efi_set_autoboot_ll_addr ( EFI_HANDLE device ) {
int efi_set_autoboot_ll_addr ( EFI_HANDLE device,
EFI_DEVICE_PATH_PROTOCOL *path ) {
EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
union {
EFI_SIMPLE_NETWORK_PROTOCOL *snp;
@ -50,6 +55,7 @@ int efi_set_autoboot_ll_addr ( EFI_HANDLE device ) {
} snp;
EFI_SIMPLE_NETWORK_MODE *mode;
EFI_STATUS efirc;
unsigned int vlan;
int rc;
/* Look for an SNP instance on the image's device handle */
@ -66,10 +72,23 @@ int efi_set_autoboot_ll_addr ( EFI_HANDLE device ) {
/* Record autoboot device */
mode = snp.snp->Mode;
set_autoboot_ll_addr ( &mode->CurrentAddress, mode->HwAddressSize );
vlan = efi_path_vlan ( path );
set_autoboot_ll_addr ( &mode->CurrentAddress, mode->HwAddressSize,
vlan );
DBGC ( device, "EFI %s found autoboot link-layer address:\n",
efi_handle_name ( device ) );
DBGC_HDA ( device, 0, &mode->CurrentAddress, mode->HwAddressSize );
if ( vlan ) {
DBGC ( device, "EFI %s found autoboot VLAN %d\n",
efi_handle_name ( device ), vlan );
}
/* Configure automatic VLAN device, if applicable */
if ( vlan && ( mode->HwAddressSize == ETH_ALEN ) ) {
vlan_auto ( &mode->CurrentAddress, vlan );
DBGC ( device, "EFI %s configured automatic VLAN %d\n",
efi_handle_name ( device ), vlan );
}
/* Close protocol */
bs->CloseProtocol ( device, &efi_simple_network_protocol_guid,

View File

@ -27,6 +27,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <errno.h>
#include <ipxe/cachedhcp.h>
#include <ipxe/efi/efi.h>
#include <ipxe/efi/efi_path.h>
#include <ipxe/efi/efi_cachedhcp.h>
#include <ipxe/efi/Protocol/PxeBaseCode.h>
@ -40,10 +41,13 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
* Record cached DHCP packet
*
* @v device Device handle
* @v path Device path
* @ret rc Return status code
*/
int efi_cachedhcp_record ( EFI_HANDLE device ) {
EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
int efi_cachedhcp_record ( EFI_HANDLE device,
EFI_DEVICE_PATH_PROTOCOL *path ) {
EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
unsigned int vlan;
union {
EFI_PXE_BASE_CODE_PROTOCOL *pxe;
void *interface;
@ -52,6 +56,9 @@ int efi_cachedhcp_record ( EFI_HANDLE device ) {
EFI_STATUS efirc;
int rc;
/* Get VLAN tag, if any */
vlan = efi_path_vlan ( path );
/* Look for a PXE base code instance on the image's device handle */
if ( ( efirc = bs->OpenProtocol ( device,
&efi_pxe_base_code_protocol_guid,
@ -75,7 +82,7 @@ int efi_cachedhcp_record ( EFI_HANDLE device ) {
/* Record DHCPACK, if present */
if ( mode->DhcpAckReceived &&
( ( rc = cachedhcp_record ( &cached_dhcpack,
( ( rc = cachedhcp_record ( &cached_dhcpack, vlan,
virt_to_user ( &mode->DhcpAck ),
sizeof ( mode->DhcpAck ) ) ) != 0 ) ) {
DBGC ( device, "EFI %s could not record DHCPACK: %s\n",
@ -85,7 +92,7 @@ int efi_cachedhcp_record ( EFI_HANDLE device ) {
/* Record ProxyDHCPOFFER, if present */
if ( mode->ProxyOfferReceived &&
( ( rc = cachedhcp_record ( &cached_proxydhcp,
( ( rc = cachedhcp_record ( &cached_proxydhcp, vlan,
virt_to_user ( &mode->ProxyOffer ),
sizeof ( mode->ProxyOffer ) ) ) != 0)){
DBGC ( device, "EFI %s could not record ProxyDHCPOFFER: %s\n",
@ -95,7 +102,7 @@ int efi_cachedhcp_record ( EFI_HANDLE device ) {
/* Record PxeBSACK, if present */
if ( mode->PxeReplyReceived &&
( ( rc = cachedhcp_record ( &cached_pxebs,
( ( rc = cachedhcp_record ( &cached_pxebs, vlan,
virt_to_user ( &mode->PxeReply ),
sizeof ( mode->PxeReply ) ) ) != 0)){
DBGC ( device, "EFI %s could not record PXEBSACK: %s\n",

View File

@ -419,14 +419,15 @@ static int efi_local_open_resolved ( struct efi_local *local,
* Open specified path
*
* @v local Local file
* @v path Path to file
* @v filename Path to file relative to our own image
* @ret rc Return status code
*/
static int efi_local_open_path ( struct efi_local *local, const char *path ) {
FILEPATH_DEVICE_PATH *fp = container_of ( efi_loaded_image->FilePath,
FILEPATH_DEVICE_PATH, Header);
size_t fp_len = ( fp ? efi_path_len ( &fp->Header ) : 0 );
char base[ fp_len / 2 /* Cannot exceed this length */ ];
static int efi_local_open_path ( struct efi_local *local,
const char *filename ) {
EFI_DEVICE_PATH_PROTOCOL *path = efi_loaded_image->FilePath;
EFI_DEVICE_PATH_PROTOCOL *next;
FILEPATH_DEVICE_PATH *fp;
char base[ efi_path_len ( path ) / 2 /* Cannot exceed this length */ ];
size_t remaining = sizeof ( base );
size_t len;
char *resolved;
@ -436,13 +437,12 @@ static int efi_local_open_path ( struct efi_local *local, const char *path ) {
/* Construct base path to our own image, if possible */
memset ( base, 0, sizeof ( base ) );
tmp = base;
while ( fp && ( fp->Header.Type != END_DEVICE_PATH_TYPE ) ) {
for ( ; ( next = efi_path_next ( path ) ) ; path = next ) {
fp = container_of ( path, FILEPATH_DEVICE_PATH, Header );
len = snprintf ( tmp, remaining, "%ls", fp->PathName );
assert ( len < remaining );
tmp += len;
remaining -= len;
fp = ( ( ( void * ) fp ) + ( ( fp->Header.Length[1] << 8 ) |
fp->Header.Length[0] ) );
}
DBGC2 ( local, "LOCAL %p base path \"%s\"\n",
local, base );
@ -454,7 +454,7 @@ static int efi_local_open_path ( struct efi_local *local, const char *path ) {
}
/* Resolve path */
resolved = resolve_path ( base, path );
resolved = resolve_path ( base, filename );
if ( ! resolved ) {
rc = -ENOMEM;
goto err_resolve;

View File

@ -193,6 +193,48 @@ void efi_nullify_nii ( EFI_NETWORK_INTERFACE_IDENTIFIER_PROTOCOL *nii ) {
nii->Id = ( ( intptr_t ) &efi_null_undi );
}
/******************************************************************************
*
* VLAN configuration protocol
*
******************************************************************************
*/
static EFI_STATUS EFIAPI
efi_null_vlan_set ( EFI_VLAN_CONFIG_PROTOCOL *vcfg __unused,
UINT16 tag __unused, UINT8 priority __unused ) {
return EFI_UNSUPPORTED;
}
static EFI_STATUS EFIAPI
efi_null_vlan_find ( EFI_VLAN_CONFIG_PROTOCOL *vcfg __unused,
UINT16 *filter __unused, UINT16 *count __unused,
EFI_VLAN_FIND_DATA **entries __unused ) {
return EFI_UNSUPPORTED;
}
static EFI_STATUS EFIAPI
efi_null_vlan_remove ( EFI_VLAN_CONFIG_PROTOCOL *vcfg __unused,
UINT16 tag __unused ) {
return EFI_UNSUPPORTED;
}
static EFI_VLAN_CONFIG_PROTOCOL efi_null_vlan = {
.Set = efi_null_vlan_set,
.Find = efi_null_vlan_find,
.Remove = efi_null_vlan_remove,
};
/**
* Nullify VLAN configuration interface
*
* @v vcfg VLAN configuration protocol
*/
void efi_nullify_vlan ( EFI_VLAN_CONFIG_PROTOCOL *vcfg ) {
memcpy ( vcfg, &efi_null_vlan, sizeof ( *vcfg ) );
}
/******************************************************************************
*
* Component name protocol

View File

@ -40,19 +40,43 @@
*
*/
/**
* Find next element in device path
*
* @v path Device path, or NULL
* @v next Next element in device path, or NULL if at end
*/
EFI_DEVICE_PATH_PROTOCOL * efi_path_next ( EFI_DEVICE_PATH_PROTOCOL *path ) {
/* Check for non-existent device path */
if ( ! path )
return NULL;
/* Check for end of device path */
if ( path->Type == END_DEVICE_PATH_TYPE )
return NULL;
/* Move to next component of the device path */
path = ( ( ( void * ) path ) +
/* There's this amazing new-fangled thing known as
* a UINT16, but who wants to use one of those? */
( ( path->Length[1] << 8 ) | path->Length[0] ) );
return path;
}
/**
* Find end of device path
*
* @v path Path to device
* @ret path_end End of device path
* @v path Device path, or NULL
* @ret path_end End of device path, or NULL
*/
EFI_DEVICE_PATH_PROTOCOL * efi_path_end ( EFI_DEVICE_PATH_PROTOCOL *path ) {
EFI_DEVICE_PATH_PROTOCOL *next;
while ( path->Type != END_DEVICE_PATH_TYPE ) {
path = ( ( ( void * ) path ) +
/* There's this amazing new-fangled thing known as
* a UINT16, but who wants to use one of those? */
( ( path->Length[1] << 8 ) | path->Length[0] ) );
/* Find end of device path */
while ( ( next = efi_path_next ( path ) ) != NULL ) {
path = next;
}
return path;
@ -61,7 +85,7 @@ EFI_DEVICE_PATH_PROTOCOL * efi_path_end ( EFI_DEVICE_PATH_PROTOCOL *path ) {
/**
* Find length of device path (excluding terminator)
*
* @v path Path to device
* @v path Device path, or NULL
* @ret path_len Length of device path
*/
size_t efi_path_len ( EFI_DEVICE_PATH_PROTOCOL *path ) {
@ -70,6 +94,29 @@ size_t efi_path_len ( EFI_DEVICE_PATH_PROTOCOL *path ) {
return ( ( ( void * ) end ) - ( ( void * ) path ) );
}
/**
* Get VLAN tag from device path
*
* @v path Device path
* @ret tag VLAN tag, or 0 if not a VLAN
*/
unsigned int efi_path_vlan ( EFI_DEVICE_PATH_PROTOCOL *path ) {
EFI_DEVICE_PATH_PROTOCOL *next;
VLAN_DEVICE_PATH *vlan;
/* Search for VLAN device path */
for ( ; ( next = efi_path_next ( path ) ) ; path = next ) {
if ( ( path->Type == MESSAGING_DEVICE_PATH ) &&
( path->SubType == MSG_VLAN_DP ) ) {
vlan = container_of ( path, VLAN_DEVICE_PATH, Header );
return vlan->VlanId;
}
}
/* No VLAN device path found */
return 0;
}
/**
* Concatenate EFI device paths
*

View File

@ -199,7 +199,7 @@ static void efi_pxe_ip_sockaddr ( struct efi_pxe *pxe, EFI_IP_ADDRESS *ip,
memset ( sockaddr, 0, sizeof ( *sockaddr ) );
sockaddr->sa.sa_family = pxe->tcpip->sa_family;
memcpy ( &sockaddr->se.se_addr, ip, pxe->net->net_addr_len );
sockaddr->se.se_scope_id = pxe->netdev->index;
sockaddr->se.se_scope_id = pxe->netdev->scope_id;
}
/**

View File

@ -934,11 +934,11 @@ static uint8_t efi_undi_checksum ( void *data, size_t len ) {
*/
static unsigned int efi_undi_ifnum ( struct efi_snp_device *snpdev ) {
/* iPXE network device indexes are one-based (leaving zero
/* iPXE network device scope IDs are one-based (leaving zero
* meaning "unspecified"). UNDI interface numbers are
* zero-based.
*/
return ( snpdev->netdev->index - 1 );
return ( snpdev->netdev->scope_id - 1 );
}
/**
@ -1488,6 +1488,164 @@ static EFI_NETWORK_INTERFACE_IDENTIFIER_PROTOCOL efi_snp_device_nii = {
.Ipv6Supported = TRUE, /* This is a raw packet interface, FFS! */
};
/******************************************************************************
*
* VLAN configuration protocol
*
******************************************************************************
*/
/**
* Create or modify VLAN device
*
* @v vcfg VLAN configuration protocol
* @v tag VLAN tag
* @v priority Default VLAN priority
* @ret efirc EFI status code
*/
static EFI_STATUS EFIAPI efi_vlan_set ( EFI_VLAN_CONFIG_PROTOCOL *vcfg,
UINT16 tag, UINT8 priority ) {
struct efi_snp_device *snpdev =
container_of ( vcfg, struct efi_snp_device, vcfg );
struct net_device *trunk = snpdev->netdev;
struct efi_saved_tpl tpl;
int rc;
/* Raise TPL */
efi_raise_tpl ( &tpl );
/* Create or modify VLAN device */
if ( ( rc = vlan_create ( trunk, tag, priority ) ) != 0 ) {
DBGC ( snpdev, "SNPDEV %p could not create VLAN tag %d: %s\n",
snpdev, tag, strerror ( rc ) );
goto err_create;
}
DBGC ( snpdev, "SNPDEV %p created VLAN tag %d priority %d\n",
snpdev, tag, priority );
err_create:
efi_restore_tpl ( &tpl );
return EFIRC ( rc );
}
/**
* Find VLAN device(s)
*
* @v vcfg VLAN configuration protocol
* @v filter VLAN tag, or NULL to find all VLANs
* @v count Number of VLANs
* @v entries List of VLANs
* @ret efirc EFI status code
*/
static EFI_STATUS EFIAPI efi_vlan_find ( EFI_VLAN_CONFIG_PROTOCOL *vcfg,
UINT16 *filter, UINT16 *count,
EFI_VLAN_FIND_DATA **entries ) {
EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
struct efi_snp_device *snpdev =
container_of ( vcfg, struct efi_snp_device, vcfg );
struct net_device *trunk = snpdev->netdev;
struct net_device *vlan;
struct efi_saved_tpl tpl;
EFI_VLAN_FIND_DATA *entry;
VOID *buffer;
unsigned int tag;
unsigned int tci;
size_t len;
EFI_STATUS efirc;
int rc;
/* Raise TPL */
efi_raise_tpl ( &tpl );
/* Count number of matching VLANs */
*count = 0;
for ( tag = 1 ; VLAN_TAG_IS_VALID ( tag ) ; tag++ ) {
if ( filter && ( tag != *filter ) )
continue;
if ( ! ( vlan = vlan_find ( trunk, tag ) ) )
continue;
(*count)++;
}
/* Allocate buffer to hold results */
len = ( (*count) * sizeof ( *entry ) );
if ( ( efirc = bs->AllocatePool ( EfiBootServicesData, len,
&buffer ) ) != 0 ) {
rc = -EEFI ( efirc );
goto err_alloc;
}
/* Fill in buffer */
*entries = buffer;
entry = *entries;
for ( tag = 1 ; VLAN_TAG_IS_VALID ( tag ) ; tag++ ) {
if ( filter && ( tag != *filter ) )
continue;
if ( ! ( vlan = vlan_find ( trunk, tag ) ) )
continue;
tci = vlan_tci ( vlan );
entry->VlanId = VLAN_TAG ( tci );
entry->Priority = VLAN_PRIORITY ( tci );
assert ( entry->VlanId == tag );
entry++;
}
assert ( entry == &(*entries)[*count] );
/* Success */
rc = 0;
err_alloc:
efi_restore_tpl ( &tpl );
return EFIRC ( rc );
}
/**
* Remove VLAN device
*
* @v vcfg VLAN configuration protocol
* @v tag VLAN tag
* @ret efirc EFI status code
*/
static EFI_STATUS EFIAPI efi_vlan_remove ( EFI_VLAN_CONFIG_PROTOCOL *vcfg,
UINT16 tag ) {
struct efi_snp_device *snpdev =
container_of ( vcfg, struct efi_snp_device, vcfg );
struct net_device *trunk = snpdev->netdev;
struct net_device *vlan;
struct efi_saved_tpl tpl;
int rc;
/* Raise TPL */
efi_raise_tpl ( &tpl );
/* Identify VLAN device */
vlan = vlan_find ( trunk, tag );
if ( ! vlan ) {
DBGC ( snpdev, "SNPDEV %p could not find VLAN tag %d\n",
snpdev, tag );
rc = -ENOENT;
goto err_find;
}
/* Remove VLAN device */
vlan_destroy ( vlan );
DBGC ( snpdev, "SNPDEV %p removed VLAN tag %d\n", snpdev, tag );
/* Success */
rc = 0;
err_find:
efi_restore_tpl ( &tpl );
return EFIRC ( rc );
}
/** VLAN configuration protocol */
static EFI_VLAN_CONFIG_PROTOCOL efi_vlan = {
.Set = efi_vlan_set,
.Find = efi_vlan_find,
.Remove = efi_vlan_remove,
};
/******************************************************************************
*
* Component name protocol
@ -1627,6 +1785,8 @@ static int efi_snp_probe ( struct net_device *netdev ) {
struct efi_snp_device *snpdev;
unsigned int ifcnt;
void *interface;
unsigned int tci;
char vlan_name[ 12 /* ", VLAN xxxx" + NUL */ ];
int leak = 0;
EFI_STATUS efirc;
int rc;
@ -1687,17 +1847,27 @@ static int efi_snp_probe ( struct net_device *netdev ) {
efi_snp_undi.Fudge -= efi_undi_checksum ( &efi_snp_undi,
sizeof ( efi_snp_undi ) );
/* Populate the VLAN configuration protocol */
memcpy ( &snpdev->vcfg, &efi_vlan, sizeof ( snpdev->vcfg ) );
/* Populate the component name structure */
efi_snprintf ( snpdev->driver_name,
( sizeof ( snpdev->driver_name ) /
sizeof ( snpdev->driver_name[0] ) ),
"%s %s", product_short_name, netdev->dev->driver_name );
tci = vlan_tci ( netdev );
if ( tci ) {
snprintf ( vlan_name, sizeof ( vlan_name ), ", VLAN %d",
VLAN_TAG ( tci ) );
} else {
vlan_name[0] = '\0';
}
efi_snprintf ( snpdev->controller_name,
( sizeof ( snpdev->controller_name ) /
sizeof ( snpdev->controller_name[0] ) ),
"%s %s (%s, %s)", product_short_name,
"%s %s (%s, %s%s)", product_short_name,
netdev->dev->driver_name, netdev->dev->name,
netdev_addr ( netdev ) );
netdev_addr ( netdev ), vlan_name );
snpdev->name2.GetDriverName = efi_snp_get_driver_name;
snpdev->name2.GetControllerName = efi_snp_get_controller_name;
snpdev->name2.SupportedLanguages = "en";
@ -1725,6 +1895,7 @@ static int efi_snp_probe ( struct net_device *netdev ) {
&efi_device_path_protocol_guid, snpdev->path,
&efi_nii_protocol_guid, &snpdev->nii,
&efi_nii31_protocol_guid, &snpdev->nii,
&efi_vlan_config_protocol_guid, &snpdev->vcfg,
&efi_component_name2_protocol_guid, &snpdev->name2,
&efi_load_file_protocol_guid, &snpdev->load_file,
NULL ) ) != 0 ) {
@ -1811,6 +1982,7 @@ static int efi_snp_probe ( struct net_device *netdev ) {
&efi_device_path_protocol_guid, snpdev->path,
&efi_nii_protocol_guid, &snpdev->nii,
&efi_nii31_protocol_guid, &snpdev->nii,
&efi_vlan_config_protocol_guid, &snpdev->vcfg,
&efi_component_name2_protocol_guid, &snpdev->name2,
&efi_load_file_protocol_guid, &snpdev->load_file,
NULL ) ) != 0 ) {
@ -1820,6 +1992,7 @@ static int efi_snp_probe ( struct net_device *netdev ) {
}
efi_nullify_snp ( &snpdev->snp );
efi_nullify_nii ( &snpdev->nii );
efi_nullify_vlan ( &snpdev->vcfg );
efi_nullify_name2 ( &snpdev->name2 );
efi_nullify_load_file ( &snpdev->load_file );
err_install_protocol_interface:
@ -1899,6 +2072,7 @@ static void efi_snp_remove ( struct net_device *netdev ) {
&efi_device_path_protocol_guid, snpdev->path,
&efi_nii_protocol_guid, &snpdev->nii,
&efi_nii31_protocol_guid, &snpdev->nii,
&efi_vlan_config_protocol_guid, &snpdev->vcfg,
&efi_component_name2_protocol_guid, &snpdev->name2,
&efi_load_file_protocol_guid, &snpdev->load_file,
NULL ) ) != 0 ) ) {
@ -1908,6 +2082,7 @@ static void efi_snp_remove ( struct net_device *netdev ) {
}
efi_nullify_snp ( &snpdev->snp );
efi_nullify_nii ( &snpdev->nii );
efi_nullify_vlan ( &snpdev->vcfg );
efi_nullify_name2 ( &snpdev->name2 );
efi_nullify_load_file ( &snpdev->load_file );
if ( ! leak )

View File

@ -78,12 +78,13 @@ EFI_STATUS EFIAPI _efi_start ( EFI_HANDLE image_handle,
*/
static void efi_init_application ( void ) {
EFI_HANDLE device = efi_loaded_image->DeviceHandle;
EFI_DEVICE_PATH_PROTOCOL *path = efi_loaded_image_path;
/* Identify autoboot device, if any */
efi_set_autoboot_ll_addr ( device );
efi_set_autoboot_ll_addr ( device, path );
/* Store cached DHCP packet, if any */
efi_cachedhcp_record ( device );
efi_cachedhcp_record ( device, path );
/* Load autoexec script, if any */
efi_autoexec_load ( device );

View File

@ -163,7 +163,7 @@ static struct ipv4_miniroute * ipv4_route ( unsigned int scope_id,
/* If destination is non-global, and the scope ID
* matches this network device, then use this route.
*/
if ( miniroute->netdev->index == scope_id )
if ( miniroute->netdev->scope_id == scope_id )
return miniroute;
} else {

View File

@ -330,7 +330,7 @@ struct ipv6_miniroute * ipv6_route ( unsigned int scope_id,
/* Skip entries with a non-matching scope ID, if
* destination specifies a scope ID.
*/
if ( scope_id && ( miniroute->netdev->index != scope_id ) )
if ( scope_id && ( miniroute->netdev->scope_id != scope_id ) )
continue;
/* Skip entries that are out of scope */
@ -789,12 +789,12 @@ static int ipv6_rx ( struct io_buffer *iobuf, struct net_device *netdev,
src.sin6.sin6_family = AF_INET6;
memcpy ( &src.sin6.sin6_addr, &iphdr->src,
sizeof ( src.sin6.sin6_addr ) );
src.sin6.sin6_scope_id = netdev->index;
src.sin6.sin6_scope_id = netdev->scope_id;
memset ( &dest, 0, sizeof ( dest ) );
dest.sin6.sin6_family = AF_INET6;
memcpy ( &dest.sin6.sin6_addr, &iphdr->dest,
sizeof ( dest.sin6.sin6_addr ) );
dest.sin6.sin6_scope_id = netdev->index;
dest.sin6.sin6_scope_id = netdev->scope_id;
iob_pull ( iobuf, hdrlen );
pshdr_csum = ipv6_pshdr_chksum ( iphdr, iob_len ( iobuf ),
next_header, TCPIP_EMPTY_CSUM );
@ -957,7 +957,7 @@ static const char * ipv6_sock_ntoa ( struct sockaddr *sa ) {
/* Identify network device, if applicable */
if ( IN6_IS_ADDR_LINKLOCAL ( in ) || IN6_IS_ADDR_MULTICAST ( in ) ) {
netdev = find_netdev_by_index ( sin6->sin6_scope_id );
netdev = find_netdev_by_scope_id ( sin6->sin6_scope_id );
netdev_name = ( netdev ? netdev->name : "UNKNOWN" );
} else {
netdev_name = NULL;
@ -1020,7 +1020,7 @@ static int ipv6_sock_aton ( const char *string, struct sockaddr *sa ) {
rc = -ENODEV;
goto err_find_netdev;
}
sin6->sin6_scope_id = netdev->index;
sin6->sin6_scope_id = netdev->scope_id;
} else if ( IN6_IS_ADDR_LINKLOCAL ( &in ) ||
IN6_IS_ADDR_MULTICAST ( &in ) ) {
@ -1031,7 +1031,7 @@ static int ipv6_sock_aton ( const char *string, struct sockaddr *sa ) {
*/
netdev = last_opened_netdev();
if ( netdev )
sin6->sin6_scope_id = netdev->index;
sin6->sin6_scope_id = netdev->scope_id;
}
/* Copy IPv6 address portion to socket address */

View File

@ -140,7 +140,7 @@ static int ndp_tx_request ( struct net_device *netdev,
/* Construct multicast destination address */
memset ( &sin6_dest, 0, sizeof ( sin6_dest ) );
sin6_dest.sin6_family = AF_INET6;
sin6_dest.sin6_scope_id = netdev->index;
sin6_dest.sin6_scope_id = netdev->scope_id;
ipv6_solicited_node ( &sin6_dest.sin6_addr, net_dest );
/* Construct neighbour header */
@ -177,7 +177,7 @@ static int ndp_tx_router_solicitation ( struct net_device *netdev ) {
/* Construct multicast destination address */
memset ( &sin6_dest, 0, sizeof ( sin6_dest ) );
sin6_dest.sin6_family = AF_INET6;
sin6_dest.sin6_scope_id = netdev->index;
sin6_dest.sin6_scope_id = netdev->scope_id;
ipv6_all_routers ( &sin6_dest.sin6_addr );
/* Construct router solicitation */

View File

@ -55,9 +55,6 @@ struct list_head net_devices = LIST_HEAD_INIT ( net_devices );
/** List of open network devices, in reverse order of opening */
static struct list_head open_net_devices = LIST_HEAD_INIT ( open_net_devices );
/** Network device index */
static unsigned int netdev_index = 0;
/** Network polling profiler */
static struct profiler net_poll_profiler __profiler = { .name = "net.poll" };
@ -723,6 +720,7 @@ int register_netdev ( struct net_device *netdev ) {
struct ll_protocol *ll_protocol = netdev->ll_protocol;
struct net_driver *driver;
struct net_device *duplicate;
unsigned int i;
uint32_t seed;
int rc;
@ -737,18 +735,6 @@ int register_netdev ( struct net_device *netdev ) {
ll_protocol->ll_header_len );
}
/* Reject network devices that are already available via a
* different hardware device.
*/
duplicate = find_netdev_by_ll_addr ( ll_protocol, netdev->ll_addr );
if ( duplicate && ( duplicate->dev != netdev->dev ) ) {
DBGC ( netdev, "NETDEV rejecting duplicate (phys %s) of %s "
"(phys %s)\n", netdev->dev->name, duplicate->name,
duplicate->dev->name );
rc = -EEXIST;
goto err_duplicate;
}
/* Reject named network devices that already exist */
if ( netdev->name[0] && ( duplicate = find_netdev ( netdev->name ) ) ) {
DBGC ( netdev, "NETDEV rejecting duplicate name %s\n",
@ -757,12 +743,21 @@ int register_netdev ( struct net_device *netdev ) {
goto err_duplicate;
}
/* Record device index and create device name */
/* Assign a unique device name, if not already set */
if ( netdev->name[0] == '\0' ) {
snprintf ( netdev->name, sizeof ( netdev->name ), "net%d",
netdev_index );
for ( i = 0 ; ; i++ ) {
snprintf ( netdev->name, sizeof ( netdev->name ),
"net%d", i );
if ( find_netdev ( netdev->name ) == NULL )
break;
}
}
/* Assign a unique non-zero scope ID */
for ( netdev->scope_id = 1 ; ; netdev->scope_id++ ) {
if ( find_netdev_by_scope_id ( netdev->scope_id ) == NULL )
break;
}
netdev->index = ++netdev_index;
/* Use least significant bits of the link-layer address to
* improve the randomness of the (non-cryptographic) random
@ -916,10 +911,6 @@ void unregister_netdev ( struct net_device *netdev ) {
DBGC ( netdev, "NETDEV %s unregistered\n", netdev->name );
list_del ( &netdev->list );
netdev_put ( netdev );
/* Reset network device index if no devices remain */
if ( list_empty ( &net_devices ) )
netdev_index = 0;
}
/** Enable or disable interrupts
@ -962,17 +953,17 @@ struct net_device * find_netdev ( const char *name ) {
}
/**
* Get network device by index
* Get network device by scope ID
*
* @v index Network device index
* @ret netdev Network device, or NULL
*/
struct net_device * find_netdev_by_index ( unsigned int index ) {
struct net_device * find_netdev_by_scope_id ( unsigned int scope_id ) {
struct net_device *netdev;
/* Identify network device by index */
list_for_each_entry ( netdev, &net_devices, list ) {
if ( netdev->index == index )
if ( netdev->scope_id == scope_id )
return netdev;
}
@ -999,27 +990,6 @@ struct net_device * find_netdev_by_location ( unsigned int bus_type,
return NULL;
}
/**
* Get network device by link-layer address
*
* @v ll_protocol Link-layer protocol
* @v ll_addr Link-layer address
* @ret netdev Network device, or NULL
*/
struct net_device * find_netdev_by_ll_addr ( struct ll_protocol *ll_protocol,
const void *ll_addr ) {
struct net_device *netdev;
list_for_each_entry ( netdev, &net_devices, list ) {
if ( ( netdev->ll_protocol == ll_protocol ) &&
( memcmp ( netdev->ll_addr, ll_addr,
ll_protocol->ll_addr_len ) == 0 ) )
return netdev;
}
return NULL;
}
/**
* Get most recently opened network device
*
@ -1171,12 +1141,12 @@ static void net_step ( struct process *process __unused ) {
}
/**
* Get the VLAN tag (when VLAN support is not present)
* Get the VLAN tag control information (when VLAN support is not present)
*
* @v netdev Network device
* @ret tag 0, indicating that device is not a VLAN device
*/
__weak unsigned int vlan_tag ( struct net_device *netdev __unused ) {
__weak unsigned int vlan_tci ( struct net_device *netdev __unused ) {
return 0;
}

View File

@ -189,7 +189,7 @@ static void peerdisc_socket_tx ( const char *uuid, const char *id ) {
/* Skip unopened network devices */
if ( ! netdev_is_open ( netdev ) )
continue;
address.st.st_scope_id = netdev->index;
address.st.st_scope_id = netdev->scope_id;
/* Discard request (for test purposes) if applicable */
if ( inject_fault ( PEERDISC_DISCARD_RATE ) )

View File

@ -955,7 +955,7 @@ int start_dhcpv6 ( struct interface *job, struct net_device *netdev,
addresses.client.sin6.sin6_port = htons ( DHCPV6_CLIENT_PORT );
addresses.server.sin6.sin6_family = AF_INET6;
ipv6_all_dhcp_relay_and_servers ( &addresses.server.sin6.sin6_addr );
addresses.server.sin6.sin6_scope_id = netdev->index;
addresses.server.sin6.sin6_scope_id = netdev->scope_id;
addresses.server.sin6.sin6_port = htons ( DHCPV6_SERVER_PORT );
/* Construct client DUID from system UUID */

View File

@ -55,6 +55,12 @@ struct vlan_device {
unsigned int priority;
};
/** Automatic VLAN device link-layer address */
static uint8_t vlan_auto_ll_addr[ETH_ALEN];
/** Automatic VLAN tag */
static unsigned int vlan_auto_tag;
/**
* Open VLAN device
*
@ -199,8 +205,7 @@ static void vlan_sync ( struct net_device *netdev ) {
* @v tag VLAN tag
* @ret netdev VLAN device, if any
*/
static struct net_device * vlan_find ( struct net_device *trunk,
unsigned int tag ) {
struct net_device * vlan_find ( struct net_device *trunk, unsigned int tag ) {
struct net_device *netdev;
struct vlan_device *vlan;
@ -288,17 +293,17 @@ struct net_protocol vlan_protocol __net_protocol = {
};
/**
* Get the VLAN tag
* Get the VLAN tag control information
*
* @v netdev Network device
* @ret tag VLAN tag, or 0 if device is not a VLAN device
* @ret tci VLAN tag control information, or 0 if not a VLAN device
*/
unsigned int vlan_tag ( struct net_device *netdev ) {
unsigned int vlan_tci ( struct net_device *netdev ) {
struct vlan_device *vlan;
if ( netdev->op == &vlan_operations ) {
vlan = netdev->priv;
return vlan->tag;
return ( VLAN_TCI ( vlan->tag, vlan->priority ) );
} else {
return 0;
}
@ -448,6 +453,47 @@ int vlan_destroy ( struct net_device *netdev ) {
return 0;
}
/**
* Configure automatic VLAN device
*
* @v ll_addr Link-layer address
* @v tag VLAN tag
*/
void vlan_auto ( const void *ll_addr, unsigned int tag ) {
/* Record link-layer address and VLAN tag */
memcpy ( vlan_auto_ll_addr, ll_addr, ETH_ALEN );
vlan_auto_tag = tag;
}
/**
* Create automatic VLAN device
*
* @v trunk Trunk network device
* @ret rc Return status code
*/
static int vlan_probe ( struct net_device *trunk ) {
int rc;
/* Do nothing unless an automatic VLAN exists */
if ( ! vlan_auto_tag )
return 0;
/* Ignore non-trunk devices */
if ( ! vlan_can_be_trunk ( trunk ) )
return 0;
/* Ignore non-matching link-layer addresses */
if ( memcmp ( trunk->ll_addr, vlan_auto_ll_addr, ETH_ALEN ) != 0 )
return 0;
/* Create automatic VLAN device */
if ( ( rc = vlan_create ( trunk, vlan_auto_tag, 0 ) ) != 0 )
return rc;
return 0;
}
/**
* Handle trunk network device link state change
*
@ -504,6 +550,7 @@ static void vlan_remove ( struct net_device *trunk ) {
/** VLAN driver */
struct net_driver vlan_driver __net_driver = {
.name = "VLAN",
.probe = vlan_probe,
.notify = vlan_notify,
.remove = vlan_remove,
};

View File

@ -127,7 +127,7 @@ static const struct in6_addr sample_multicast = {
/** Dummy network device used for routing tests */
static struct net_device ipv6_test_netdev = {
.refcnt = REF_INIT ( ref_no_free ),
.index = 42,
.scope_id = 42,
.state = NETDEV_OPEN,
};
@ -349,7 +349,7 @@ static void ipv6_route_okx ( struct ipv6_test_table *table, const char *dest,
/* Perform routing */
actual = &in_dest;
miniroute = ipv6_route ( ipv6_test_netdev.index, &actual );
miniroute = ipv6_route ( ipv6_test_netdev.scope_id, &actual );
/* Validate result */
if ( src ) {

View File

@ -27,6 +27,7 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
#include <stdio.h>
#include <errno.h>
#include <ipxe/netdevice.h>
#include <ipxe/vlan.h>
#include <ipxe/dhcp.h>
#include <ipxe/settings.h>
#include <ipxe/image.h>
@ -57,6 +58,9 @@ FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
/** Link-layer address of preferred autoboot device, if known */
static uint8_t autoboot_ll_addr[MAX_LL_ADDR_LEN];
/** VLAN tag of preferred autoboot device, if known */
static unsigned int autoboot_vlan;
/** Device location of preferred autoboot device, if known */
static struct device_description autoboot_desc;
@ -494,8 +498,9 @@ void set_autoboot_busloc ( unsigned int bus_type, unsigned int location ) {
*/
static int is_autoboot_ll_addr ( struct net_device *netdev ) {
return ( memcmp ( netdev->ll_addr, autoboot_ll_addr,
netdev->ll_protocol->ll_addr_len ) == 0 );
return ( ( memcmp ( netdev->ll_addr, autoboot_ll_addr,
netdev->ll_protocol->ll_addr_len ) == 0 ) &&
( vlan_tag ( netdev ) == autoboot_vlan ) );
}
/**
@ -503,14 +508,19 @@ static int is_autoboot_ll_addr ( struct net_device *netdev ) {
*
* @v ll_addr Link-layer address
* @v len Length of link-layer address
* @v vlan VLAN tag
*/
void set_autoboot_ll_addr ( const void *ll_addr, size_t len ) {
void set_autoboot_ll_addr ( const void *ll_addr, size_t len,
unsigned int vlan ) {
/* Record autoboot link-layer address (truncated if necessary) */
if ( len > sizeof ( autoboot_ll_addr ) )
len = sizeof ( autoboot_ll_addr );
memcpy ( autoboot_ll_addr, ll_addr, len );
/* Record autoboot VLAN tag */
autoboot_vlan = vlan;
/* Mark autoboot device as present */
is_autoboot_device = is_autoboot_ll_addr;
}