diff --git a/src/include/ipxe/netdevice.h b/src/include/ipxe/netdevice.h index 6ef9cb1e..95ad1cf1 100644 --- a/src/include/ipxe/netdevice.h +++ b/src/include/ipxe/netdevice.h @@ -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, diff --git a/src/net/netdevice.c b/src/net/netdevice.c index 2350256f..a8020858 100644 --- a/src/net/netdevice.c +++ b/src/net/netdevice.c @@ -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 *