diff options
Diffstat (limited to 'src/shared/netif-util.c')
-rw-r--r-- | src/shared/netif-util.c | 206 |
1 files changed, 206 insertions, 0 deletions
diff --git a/src/shared/netif-util.c b/src/shared/netif-util.c new file mode 100644 index 0000000..f56c564 --- /dev/null +++ b/src/shared/netif-util.c @@ -0,0 +1,206 @@ +/* SPDX-License-Identifier: LGPL-2.1-or-later */ + +#include <linux/if.h> +#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" +#include "strv.h" + +bool netif_has_carrier(uint8_t operstate, unsigned flags) { + /* see Documentation/networking/operstates.txt in the kernel sources */ + + if (operstate == IF_OPER_UP) + return true; + + if (operstate != IF_OPER_UNKNOWN) + return false; + + /* operstate may not be implemented, so fall back to flags */ + return FLAGS_SET(flags, IFF_LOWER_UP | IFF_RUNNING) && + !FLAGS_SET(flags, IFF_DORMANT); +} + +int net_get_type_string(sd_device *device, uint16_t iftype, char **ret) { + const char *t; + char *p; + + if (device && + sd_device_get_devtype(device, &t) >= 0 && + !isempty(t)) { + p = strdup(t); + if (!p) + return -ENOMEM; + + *ret = p; + return 0; + } + + t = arphrd_to_name(iftype); + if (!t) + return -ENOENT; + + p = strdup(t); + if (!p) + return -ENOMEM; + + *ret = ascii_strlower(p); + return 0; +} + +const char *net_get_persistent_name(sd_device *device) { + assert(device); + + /* fetch some persistent data unique (on this machine) to this device */ + FOREACH_STRING(field, "ID_NET_NAME_ONBOARD", "ID_NET_NAME_SLOT", "ID_NET_NAME_PATH", "ID_NET_NAME_MAC") { + const char *name; + + if (sd_device_get_property_value(device, field, &name) >= 0) + return name; + } + + return NULL; +} + +/* Used when generating hardware address by udev, and IPv4LL seed by networkd. */ +#define HASH_KEY SD_ID128_MAKE(d3,1e,48,fa,90,fe,4b,4c,9d,af,d5,d7,a1,b1,2e,8a) + +int net_get_unique_predictable_data(sd_device *device, bool use_sysname, uint64_t *ret) { + const char *name; + + assert(device); + assert(ret); + + /* net_get_persistent_name() will return one of the device names based on stable information about + * the device. If this is not available, we fall back to using the actual device name. */ + name = net_get_persistent_name(device); + if (!name && use_sysname) + (void) sd_device_get_sysname(device, &name); + if (!name) + return log_device_debug_errno(device, SYNTHETIC_ERRNO(ENODATA), + "No stable identifying information found"); + + log_device_debug(device, "Using \"%s\" as stable identifying information", name); + + return net_get_unique_predictable_data_from_name(name, &HASH_KEY, ret); +} + +int net_get_unique_predictable_data_from_name( + const char *name, + const sd_id128_t *key, + uint64_t *ret) { + + size_t l, sz; + uint8_t *v; + int r; + + assert(name); + assert(key); + assert(ret); + + l = strlen(name); + sz = sizeof(sd_id128_t) + l; + v = newa(uint8_t, sz); + + /* Fetch some persistent data unique to this machine */ + r = sd_id128_get_machine((sd_id128_t*) v); + if (r < 0) + return r; + + memcpy(v + sizeof(sd_id128_t), name, l); + + /* Let's hash the machine ID plus the device name. We use + * a fixed, but originally randomly created hash key here. */ + *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 is_static, + 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 (is_static) + 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 (is_static) + log_link_warning(&link, "Specified MAC address is null, refusing."); + return -EINVAL; + } + + if (ether_addr_is_broadcast(&new_hw_addr->ether)) { + if (is_static) + log_link_warning(&link, "Specified MAC address is broadcast, refusing."); + return -EINVAL; + } + + if (ether_addr_is_multicast(&new_hw_addr->ether)) { + if (is_static) + log_link_warning(&link, "Specified MAC address has the multicast bit set, clearing the bit."); + + new_hw_addr->bytes[0] &= 0xfe; + } + + if (!is_static && !ether_addr_is_local(&new_hw_addr->ether)) + /* Adjust local assignment bit when the MAC address is generated randomly. */ + 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 (is_static && + (!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 (is_static) + 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 (is_static) + log_link_warning(&link, "Unsupported interface type %s%u to set MAC address, refusing.", + strna(arphrd_to_name(iftype)), iftype); + return -EINVAL; + } + + return 0; +} |