]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
netif-util: introduce net_verify_hardware_address()
authorYu Watanabe <watanabe.yu+github@gmail.com>
Sat, 6 Nov 2021 01:55:21 +0000 (10:55 +0900)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Thu, 25 Nov 2021 12:07:32 +0000 (21:07 +0900)
src/shared/netif-util.c
src/shared/netif-util.h

index 85b6ad45c8bcab4cd1eccad6ed19bfeae09b01a1..8d3d4473fa6111f77dd840e29c372e9a5a4d6c7a 100644 (file)
@@ -1,7 +1,11 @@
 /* SPDX-License-Identifier: LGPL-2.1-or-later */
 
+#include <linux/if_arp.h>
+
 #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;
+}
index f239f042b58d9219f08e0b8d5ecf30846b2c22b2..09776811538d745e20ba600f60e1f1e88c75286c 100644 (file)
@@ -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);