| /* 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; |
| } |