]> git.ipfire.org Git - thirdparty/ipxe.git/commitdiff
[netdevice] Avoid registering duplicate network devices
authorMichael Brown <mcb30@ipxe.org>
Wed, 30 Jul 2014 17:11:20 +0000 (18:11 +0100)
committerMichael Brown <mcb30@ipxe.org>
Wed, 30 Jul 2014 17:22:09 +0000 (18:22 +0100)
Reject network devices which appear to be duplicates of those already
available via a different underlying hardware device.  On a Xen PV-HVM
system, this allows us to filter out the emulated PCI NICs (which
would otherwise appear alongside the netfront NICs).

Note that we cannot use the Xen facility to "unplug" the emulated PCI
NICs, since there is no guarantee that the OS we subsequently load
will have a native netfront driver.

We permit devices with the same MAC address if they are attached to
the same underlying hardware device (e.g. VLAN devices).

Inspired-by: Marin Hannache <git@mareo.fr>
Signed-off-by: Michael Brown <mcb30@ipxe.org>
src/include/ipxe/netdevice.h
src/net/netdevice.c

index 6ef9cb1e51f8f827fc451fb6726546643ce6a005..95ad1cf1bcb3683296da1e6b809f5b05258e192d 100644 (file)
@@ -685,6 +685,8 @@ 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_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 2350256f9610a7b1828aca06d84750be316a12d9..a8020858e8791523d280012e382335dd215006c4 100644 (file)
@@ -602,9 +602,27 @@ struct net_device * alloc_netdev ( size_t priv_len ) {
 int register_netdev ( struct net_device *netdev ) {
        struct ll_protocol *ll_protocol = netdev->ll_protocol;
        struct net_driver *driver;
+       struct net_device *duplicate;
        uint32_t seed;
        int rc;
 
+       /* Set initial link-layer address, if not already set */
+       if ( ! netdev_has_ll_addr ( netdev ) ) {
+               ll_protocol->init_addr ( netdev->hw_addr, netdev->ll_addr );
+       }
+
+       /* 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;
+       }
+
        /* Record device index and create device name */
        netdev->index = netdev_index++;
        if ( netdev->name[0] == '\0' ) {
@@ -612,11 +630,6 @@ int register_netdev ( struct net_device *netdev ) {
                           netdev->index );
        }
 
-       /* Set initial link-layer address, if not already set */
-       if ( ! netdev_has_ll_addr ( netdev ) ) {
-               ll_protocol->init_addr ( netdev->hw_addr, netdev->ll_addr );
-       }
-
        /* Use least significant bits of the link-layer address to
         * improve the randomness of the (non-cryptographic) random
         * number generator.
@@ -660,6 +673,7 @@ int register_netdev ( struct net_device *netdev ) {
        clear_settings ( netdev_settings ( netdev ) );
        unregister_settings ( netdev_settings ( netdev ) );
  err_register_settings:
+ err_duplicate:
        return rc;
 }
 
@@ -852,6 +866,27 @@ 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
  *