From 37593b7c488f7b957936500158f200af16534c6b Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Sat, 6 Nov 2021 10:55:21 +0900 Subject: [PATCH 1/2] netif-util: introduce net_verify_hardware_address() --- src/shared/netif-util.c | 92 +++++++++++++++++++++++++++++++++++++++++ src/shared/netif-util.h | 8 ++++ 2 files changed, 100 insertions(+) diff --git a/src/shared/netif-util.c b/src/shared/netif-util.c index 85b6ad45c8..8d3d4473fa 100644 --- a/src/shared/netif-util.c +++ b/src/shared/netif-util.c @@ -1,7 +1,11 @@ /* SPDX-License-Identifier: LGPL-2.1-or-later */ +#include + #include "arphrd-util.h" #include "device-util.h" +#include "log-link.h" +#include "memory-util.h" #include "netif-util.h" #include "siphash24.h" #include "sparse-endian.h" @@ -99,3 +103,91 @@ int net_get_unique_predictable_data_from_name( *ret = htole64(siphash24(v, sz, key->bytes)); return 0; } + +typedef struct Link { + const char *ifname; +} Link; + +int net_verify_hardware_address( + const char *ifname, + bool warn_invalid, + uint16_t iftype, + const struct hw_addr_data *ib_hw_addr, /* current or parent HW address */ + struct hw_addr_data *new_hw_addr) { + + Link link = { .ifname = ifname }; + + assert(new_hw_addr); + + if (new_hw_addr->length == 0) + return 0; + + if (new_hw_addr->length != arphrd_to_hw_addr_len(iftype)) { + if (warn_invalid) + log_link_warning(&link, + "Specified MAC address with invalid length (%zu, expected %zu), refusing.", + new_hw_addr->length, arphrd_to_hw_addr_len(iftype)); + return -EINVAL; + } + + switch (iftype) { + case ARPHRD_ETHER: + /* see eth_random_addr() in the kernel */ + + if (ether_addr_is_null(&new_hw_addr->ether)) { + if (warn_invalid) + log_link_warning(&link, "Specified MAC address is null, refusing."); + return -EINVAL; + } + + if (ether_addr_is_broadcast(&new_hw_addr->ether)) { + if (warn_invalid) + log_link_warning(&link, "Specified MAC address is broadcast, refusing."); + return -EINVAL; + } + + if (ether_addr_is_multicast(&new_hw_addr->ether)) { + if (warn_invalid) + log_link_warning(&link, "Specified MAC address has multicast bit set, clearing the bit."); + + new_hw_addr->bytes[0] &= 0xfe; + } + + if (!ether_addr_is_local(&new_hw_addr->ether)) { + if (warn_invalid) + log_link_warning(&link, "Specified MAC address has not local assignment bit set, setting the bit."); + + new_hw_addr->bytes[0] |= 0x02; + } + + break; + + case ARPHRD_INFINIBAND: + /* see ipoib_check_lladdr() in the kernel */ + + assert(ib_hw_addr); + assert(ib_hw_addr->length == INFINIBAND_ALEN); + + if (warn_invalid && + (!memeqzero(new_hw_addr->bytes, INFINIBAND_ALEN - 8) || + memcmp(new_hw_addr->bytes, ib_hw_addr->bytes, INFINIBAND_ALEN - 8) != 0)) + log_link_warning(&link, "Only the last 8 bytes of the InifniBand MAC address can be changed, ignoring the first 12 bytes."); + + if (memeqzero(new_hw_addr->bytes + INFINIBAND_ALEN - 8, 8)) { + if (warn_invalid) + log_link_warning(&link, "The last 8 bytes of the InfiniBand MAC address cannot be null, refusing."); + return -EINVAL; + } + + memcpy(new_hw_addr->bytes, ib_hw_addr->bytes, INFINIBAND_ALEN - 8); + break; + + default: + if (warn_invalid) + log_link_warning(&link, "Unsupported interface type %s%u to set MAC address, refusing.", + strna(arphrd_to_name(iftype)), iftype); + return -EINVAL; + } + + return 0; +} diff --git a/src/shared/netif-util.h b/src/shared/netif-util.h index f239f042b5..0977681153 100644 --- a/src/shared/netif-util.h +++ b/src/shared/netif-util.h @@ -7,7 +7,15 @@ #include "sd-device.h" #include "sd-id128.h" +#include "ether-addr-util.h" + int net_get_type_string(sd_device *device, uint16_t iftype, char **ret); const char *net_get_persistent_name(sd_device *device); int net_get_unique_predictable_data(sd_device *device, bool use_sysname, uint64_t *ret); int net_get_unique_predictable_data_from_name(const char *name, const sd_id128_t *key, uint64_t *ret); +int net_verify_hardware_address( + const char *ifname, + bool warn_invalid, + uint16_t iftype, + const struct hw_addr_data *ib_hw_addr, + struct hw_addr_data *new_hw_addr); From a8840714f3ed6964bfa0a472f3bd250df6051e77 Mon Sep 17 00:00:00 2001 From: Yu Watanabe Date: Fri, 5 Nov 2021 03:20:29 +0900 Subject: [PATCH 2/2] network: make MACAddress= takes hardware address with its length is INFINIBAND_ALEN Also, the multicast and local bits in the specified MAC address for ethernet are adjusted. --- src/network/networkd-link.h | 1 + src/network/networkd-network-gperf.gperf | 2 +- src/network/networkd-network.c | 1 - src/network/networkd-network.h | 2 +- src/network/networkd-setlink.c | 24 +++++++++++---------- test/test-network/systemd-networkd-tests.py | 3 ++- 6 files changed, 18 insertions(+), 15 deletions(-) diff --git a/src/network/networkd-link.h b/src/network/networkd-link.h index edf93d720e..072c39f387 100644 --- a/src/network/networkd-link.h +++ b/src/network/networkd-link.h @@ -56,6 +56,7 @@ typedef struct Link { struct hw_addr_data hw_addr; struct hw_addr_data bcast_addr; struct hw_addr_data permanent_hw_addr; + struct hw_addr_data requested_hw_addr; struct in6_addr ipv6ll_address; uint32_t mtu; uint32_t min_mtu; diff --git a/src/network/networkd-network-gperf.gperf b/src/network/networkd-network-gperf.gperf index 2fdfb16689..331b88b69b 100644 --- a/src/network/networkd-network-gperf.gperf +++ b/src/network/networkd-network-gperf.gperf @@ -61,7 +61,7 @@ Match.KernelCommandLine, config_parse_net_condition, Match.KernelVersion, config_parse_net_condition, CONDITION_KERNEL_VERSION, offsetof(Network, conditions) Match.Architecture, config_parse_net_condition, CONDITION_ARCHITECTURE, offsetof(Network, conditions) Match.Firmware, config_parse_net_condition, CONDITION_FIRMWARE, offsetof(Network, conditions) -Link.MACAddress, config_parse_ether_addr, 0, offsetof(Network, mac) +Link.MACAddress, config_parse_hw_addr, 0, offsetof(Network, hw_addr) Link.MTUBytes, config_parse_mtu, AF_UNSPEC, offsetof(Network, mtu) Link.Group, config_parse_link_group, 0, 0 Link.ARP, config_parse_tristate, 0, offsetof(Network, arp) diff --git a/src/network/networkd-network.c b/src/network/networkd-network.c index 82094a49b1..c97998848c 100644 --- a/src/network/networkd-network.c +++ b/src/network/networkd-network.c @@ -674,7 +674,6 @@ static Network *network_free(Network *network) { set_free(network->dhcp_allow_listed_ip); set_free(network->dhcp_request_options); set_free(network->dhcp6_request_options); - free(network->mac); free(network->dhcp6_mudurl); strv_free(network->dhcp6_user_class); strv_free(network->dhcp6_vendor_class); diff --git a/src/network/networkd-network.h b/src/network/networkd-network.h index 626b7710f0..942210470c 100644 --- a/src/network/networkd-network.h +++ b/src/network/networkd-network.h @@ -94,7 +94,7 @@ struct Network { Hashmap *stacked_netdev_names; /* [Link] section */ - struct ether_addr *mac; + struct hw_addr_data hw_addr; uint32_t mtu; int32_t group; int arp; diff --git a/src/network/networkd-setlink.c b/src/network/networkd-setlink.c index 4ec47a495f..8b44965409 100644 --- a/src/network/networkd-setlink.c +++ b/src/network/networkd-setlink.c @@ -6,6 +6,7 @@ #include #include "missing_network.h" +#include "netif-util.h" #include "netlink-util.h" #include "networkd-address.h" #include "networkd-can.h" @@ -183,7 +184,7 @@ static int link_set_mac_allow_retry_handler(sd_netlink *rtnl, sd_netlink_message return 0; } - /* set_link_mac_handler() also decrement set_link_messages, so once increment the value. */ + /* set_link_mac_handler() also decrements set_link_messages, so increment the value once. */ link->set_link_messages++; return link_set_mac_handler(rtnl, m, link); } @@ -463,7 +464,7 @@ static int link_configure( return log_link_debug_errno(link, r, "Could not append IFLA_GROUP attribute: %m"); break; case SET_LINK_MAC: - r = sd_netlink_message_append_ether_addr(req, IFLA_ADDRESS, link->network->mac); + r = netlink_message_append_hw_addr(req, IFLA_ADDRESS, &link->requested_hw_addr); if (r < 0) return log_link_debug_errno(link, r, "Could not append IFLA_ADDRESS attribute: %m"); break; @@ -542,7 +543,7 @@ static bool link_is_ready_to_call_set_link(Request *req) { break; case SET_LINK_MAC: if (req->netlink_handler == link_set_mac_handler) { - /* This is the second trial to set MTU. On the first attempt + /* This is the second trial to set hardware address. On the first attempt * req->netlink_handler points to link_set_mac_allow_retry_handler(). * The first trial failed as the interface was up. */ r = link_down(link); @@ -777,20 +778,21 @@ int link_request_to_set_group(Link *link) { } int link_request_to_set_mac(Link *link, bool allow_retry) { + int r; + assert(link); assert(link->network); - if (!link->network->mac) + if (link->network->hw_addr.length == 0) return 0; - if (link->hw_addr.length != sizeof(struct ether_addr)) { - /* Note that for now we only support changing hardware addresses on Ethernet. */ - log_link_debug(link, "Size of the hardware address (%zu) does not match the size of MAC address (%zu), ignoring.", - link->hw_addr.length, sizeof(struct ether_addr)); - return 0; - } + link->requested_hw_addr = link->network->hw_addr; + r = net_verify_hardware_address(link->ifname, /* warn_invalid = */ true, + link->iftype, &link->hw_addr, &link->requested_hw_addr); + if (r < 0) + return r; - if (ether_addr_equal(&link->hw_addr.ether, link->network->mac)) + if (hw_addr_equal(&link->hw_addr, &link->requested_hw_addr)) return 0; return link_request_set_link(link, SET_LINK_MAC, diff --git a/test/test-network/systemd-networkd-tests.py b/test/test-network/systemd-networkd-tests.py index 9f3d1dce38..d7ec7e9e25 100755 --- a/test/test-network/systemd-networkd-tests.py +++ b/test/test-network/systemd-networkd-tests.py @@ -2704,7 +2704,8 @@ class NetworkdNetworkTests(unittest.TestCase, Utilities): output = check_output('ip link show dummy98') print(output) - self.assertRegex(output, '00:01:02:aa:bb:cc') + # 00:01:02:aa:bb:cc was requested, and the local bit is set by networkd. + self.assertRegex(output, '02:01:02:aa:bb:cc') def test_ip_link_unmanaged(self): copy_unit_to_networkd_unit_path('25-link-section-unmanaged.network', '12-dummy.netdev')