From caeec0de6d460c1c870dc7a981c46813ceca1d57 Mon Sep 17 00:00:00 2001 From: Nick Rosbrook Date: Thu, 19 Mar 2026 11:12:06 -0400 Subject: [PATCH 1/2] ether-addr-util: introduce hw_addr_is_valid() Take this from udev, and adapt it to make it re-usable elsewhere. --- src/basic/ether-addr-util.c | 24 ++++++++++++++++++++++++ src/basic/ether-addr-util.h | 1 + src/udev/net/link-config.c | 24 ++---------------------- 3 files changed, 27 insertions(+), 22 deletions(-) diff --git a/src/basic/ether-addr-util.c b/src/basic/ether-addr-util.c index 375c044415..2a8196d530 100644 --- a/src/basic/ether-addr-util.c +++ b/src/basic/ether-addr-util.c @@ -1,6 +1,7 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ #include +#include #include #include "ether-addr-util.h" @@ -68,6 +69,29 @@ bool hw_addr_is_null(const struct hw_addr_data *addr) { return addr->length == 0 || memeqzero(addr->bytes, addr->length); } +bool hw_addr_is_valid(const struct hw_addr_data *addr, uint16_t iftype) { + assert(addr); + + switch (iftype) { + case ARPHRD_ETHER: + /* Refuse all zero and all 0xFF. */ + if (addr->length != ETH_ALEN) + return false; + + return !ether_addr_is_null(&addr->ether) && !ether_addr_is_broadcast(&addr->ether); + + case ARPHRD_INFINIBAND: + /* The last 8 bytes cannot be zero. */ + if (addr->length != INFINIBAND_ALEN) + return false; + + return !memeqzero(addr->bytes + INFINIBAND_ALEN - 8, 8); + + default: + return false; + } +} + DEFINE_HASH_OPS(hw_addr_hash_ops, struct hw_addr_data, hw_addr_hash_func, hw_addr_compare); DEFINE_HASH_OPS_WITH_KEY_DESTRUCTOR(hw_addr_hash_ops_free, struct hw_addr_data, hw_addr_hash_func, hw_addr_compare, free); diff --git a/src/basic/ether-addr-util.h b/src/basic/ether-addr-util.h index cf014a9527..d342b5621e 100644 --- a/src/basic/ether-addr-util.h +++ b/src/basic/ether-addr-util.h @@ -57,6 +57,7 @@ static inline bool hw_addr_equal(const struct hw_addr_data *a, const struct hw_a return hw_addr_compare(a, b) == 0; } bool hw_addr_is_null(const struct hw_addr_data *addr) _pure_; +bool hw_addr_is_valid(const struct hw_addr_data *addr, uint16_t iftype); extern const struct hash_ops hw_addr_hash_ops; extern const struct hash_ops hw_addr_hash_ops_free; diff --git a/src/udev/net/link-config.c b/src/udev/net/link-config.c index 704a38831e..3c4b886e67 100644 --- a/src/udev/net/link-config.c +++ b/src/udev/net/link-config.c @@ -558,26 +558,6 @@ static int link_apply_ethtool_settings(Link *link, int *ethtool_fd) { return 0; } -static bool hw_addr_is_valid(Link *link, const struct hw_addr_data *hw_addr) { - assert(link); - assert(hw_addr); - - switch (link->iftype) { - case ARPHRD_ETHER: - /* Refuse all zero and all 0xFF. */ - assert(hw_addr->length == ETH_ALEN); - return !ether_addr_is_null(&hw_addr->ether) && !ether_addr_is_broadcast(&hw_addr->ether); - - case ARPHRD_INFINIBAND: - /* The last 8 bytes cannot be zero. */ - assert(hw_addr->length == INFINIBAND_ALEN); - return !memeqzero(hw_addr->bytes + INFINIBAND_ALEN - 8, 8); - - default: - assert_not_reached(); - } -} - static int link_generate_new_hw_addr(Link *link, struct hw_addr_data *ret) { struct hw_addr_data hw_addr = HW_ADDR_NULL; bool is_static = false; @@ -647,7 +627,7 @@ static int link_generate_new_hw_addr(Link *link, struct hw_addr_data *ret) { * systems booting up at the very same time. */ for (;;) { random_bytes(p, len); - if (hw_addr_is_valid(link, &hw_addr)) + if (hw_addr_is_valid(&hw_addr, link->iftype)) break; } @@ -662,7 +642,7 @@ static int link_generate_new_hw_addr(Link *link, struct hw_addr_data *ret) { assert(len <= sizeof(result)); memcpy(p, &result, len); - if (!hw_addr_is_valid(link, &hw_addr)) + if (!hw_addr_is_valid(&hw_addr, link->iftype)) return log_link_warning_errno(link, SYNTHETIC_ERRNO(EINVAL), "Could not generate valid persistent MAC address."); } From cceddc41fd2511db52456835f24ea7e6381fab58 Mon Sep 17 00:00:00 2001 From: Nick Rosbrook Date: Tue, 24 Feb 2026 14:05:05 -0500 Subject: [PATCH 2/2] network-generator: support BOOTIF= and rd.bootif=0 options The network generator currently supports many of the options described by dracut.cmdline(7), but not everything. This commit adds support for the BOOTIF= option (and the related rd.bootif= option) used in PXE setups. This is implemented by treating BOOTIF as a special name/placeholder when used as an interface name, and expecting a MAC address to be set in the BOOTIF= parameter. The resulting .network file then uses MACAddress= in the [Match] section, instead of Name=. --- man/systemd-network-generator.service.xml | 19 +++- .../generator/network-generator-main.c | 2 + src/network/generator/network-generator.c | 97 ++++++++++++++++++- src/network/generator/network-generator.h | 6 ++ .../generator/test-network-generator.c | 83 ++++++++++++++++ 5 files changed, 204 insertions(+), 3 deletions(-) diff --git a/man/systemd-network-generator.service.xml b/man/systemd-network-generator.service.xml index ccdb57b62b..7b20f8516d 100644 --- a/man/systemd-network-generator.service.xml +++ b/man/systemd-network-generator.service.xml @@ -100,10 +100,27 @@ + + BOOTIF= + rd.bootif= + + When BOOTIF is specified in the interface field of ip=, it is treated + as a special placeholder rather than a real interface name. Then, in combination with the MAC address provided + by BOOTIF=, this is translated into a + systemd.network5 file + which matches on the the provided MAC address. When rd.bootif=0 is passed, this functionality + is disabled, and the BOOTIF= option is ignored. + + See dracut.cmdline7 + for details on the usage of these options. + + + + +