]> git.ipfire.org Git - thirdparty/ipxe.git/commitdiff
[netdevice] Allow duplicate MAC addresses
authorMichael Brown <mcb30@ipxe.org>
Sat, 14 Jan 2023 00:31:54 +0000 (00:31 +0000)
committerMichael Brown <mcb30@ipxe.org>
Sun, 15 Jan 2023 00:42:52 +0000 (00:42 +0000)
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>
src/drivers/net/ecm.c
src/drivers/net/netfront.c
src/drivers/net/netfront.h
src/include/ipxe/netdevice.h
src/net/netdevice.c

index 68ac962ab22da1090f833db3285e21932340494c..ab1f9837017346a8a7664dd3d71244da4b0e6db5 100644 (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 ) );
index 1203e585c92b1f6a54a450102169f3fd629347e3..90930a5a3467fb8a644e772eb6050ce43d39f5f5 100644 (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,
+};
index dca3ff1c519e47b9ae861f8b13ed218b03b11c20..de16d5291a4343f7f224eab9b5916a23bbcf9c98 100644 (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 */
index 29358dba067bc4bca7f519c0217af22016e03bc1..af932c25984ab9ba270901ca100ab973c731491a 100644 (file)
@@ -729,8 +729,6 @@ extern struct net_device * find_netdev ( const char *name );
 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,
index 597c622859f0322f21fc880f9971f2bd52513cd0..07961bf2033897d9ae9596b2b5e097f3840ab59d 100644 (file)
@@ -735,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",
@@ -1002,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
  *