1
0
mirror of https://github.com/systemd/systemd synced 2026-03-30 11:44:49 +02:00

Compare commits

...

25 Commits

Author SHA1 Message Date
Lennart Poettering
40258ae061
Merge pull request #20970 from poettering/token-timeout
cryptsetup: add a timeout for waiting for FIDO2/PKCS#11/TPM2 devices
2021-10-11 16:28:58 +02:00
Bogdan Seniuc
599be274c1 virt: Fix Xen PV detection when nested inside another hypervisor
Currently, when Xen PV domains are nested within a hypervisor which is
detected through CPUID (such as VMware), the detected hypervisor might
not be Xen, because we don't check for Xen until after the CPUID check.

This change moves the Xen check before CPUID checks to fix the issue,
and moves Dom0 checking to detect_vm_xen so that we keep ignoring Xen
when we are in Dom0.
2021-10-11 15:10:46 +02:00
Max Resch
a6089431d5 sd-stub: Provide initrd with LINUX_EFI_INITRD_MEDIA_GUID
Register a LINUX_EFI_INITRD_MEDIA_GUID DevicePath with a LoadFile2Protocol interface and serve the initrd to a supported Linux kernel (Version 5.8+)
Leave the x86 code for older kernels in place until supported kernels become more mainstream
2021-10-11 14:40:49 +02:00
Lennart Poettering
b1967fb83a
Merge pull request #20979 from poettering/ac-power-tweak
tweaks to ac_power()
2021-10-11 14:04:51 +02:00
Lennart Poettering
c19a51bec4 util: invert ac_power() source type check
So far we assumed every power source was a battery except for the ones
which definitely are not. I think this logic makes little sense, as
"battery" is kinda the exceptional case here, not the other way round.
Hence let's invert the type check, and denylist "Battery" devices rather
than allowlist "Mains" devices.

This should increase compatibility with alternative types of power
sources, in particular USB ones.

This takes into account that additional power types have been added
since we wrote the original code, and in particular should cover the
siutation discussed here OK:

https://sources.debian.org/src/powermgmt-base/1.36/power_supply.txt/#L31
https://sources.debian.org/src/powermgmt-base/1.36/on_ac_power/#L25

Also, modernizes the code in various was ways.

Inspired by and fixes: #20964
2021-10-11 11:31:52 +02:00
Lennart Poettering
ccd25f41f5 docs: document $SYSTEMD_CRYPTSETUP_USE_TOKEN_MODULE 2021-10-11 11:15:08 +02:00
Lennart Poettering
2c7ec8203e man: document new token-timeout= setting 2021-10-11 11:12:29 +02:00
Lennart Poettering
a2236110c3 cryptsetup: minor modernizations 2021-10-11 11:12:29 +02:00
Lennart Poettering
5cbe70af02 cryptsetup: add a configurable token waiting timeout
Let's add  configurable timeout how long to wait for FIDO2/PKCS#11
devices to show up. Once the timeout is hit, let's automatically revert
to querying via passphrase.

Fixes: #19739
2021-10-11 11:12:29 +02:00
Lennart Poettering
40091021c3 cryptsetup: before querying user for a PIN, check if a FIDO2 device is actually plugged in
Before we'd already ask for a PIN just because we know we'll need it
when the token is plugged in. We'd only the try to talk to the device
and notice it actually isn't plugged in. This is quite confusing, as
querying for the PIN suggests we already had a device we are talking to.

Let's hence check if there's actually device before we ask the PIN
question. And if there is none, let's immediately inform the caller, so
that they watch udev and retry once a device has shown up.
2021-10-11 11:12:29 +02:00
Lennart Poettering
4f0cfa7741 libfido2-util: add helper that checks whether a FIDO2 device is plugged in 2021-10-11 11:12:29 +02:00
Lennart Poettering
64c590fb06 cryptsetup: optionally turn off token module support in libcryptsetup
This is useful for debugging purposes.
2021-10-11 11:12:29 +02:00
Lennart Poettering
92828080fb cryptsetup: don't repeat exact same code twice
let's move turning off of the cache bit into the for loop, so that we
can eliminate a copy of the loop body.
2021-10-11 11:12:29 +02:00
Lennart Poettering
6bfd44ee04 fileio: add read_virtual_file_at() flavour that takes dir_fd/path pair 2021-10-11 10:58:50 +02:00
Zbigniew Jędrzejewski-Szmek
54ccd706ba
Merge pull request #20744 from yuwata/udev-netlink
udev: use netlink more aggressively

I'm pasting the comment from https://github.com/systemd/systemd/pull/20744#issuecomment-934485287
which is quite informative. The code wasn't changed significantly since then:

atenart commented 6 days ago:
> I ran tests without (93caec7) and with this PR (06735f2) on Fedora, having a few udev rules
> using attributes eligible to be cached and creating 50 veth on 4 CPUs. Although the time spent
> running the test is variable between runs, I generally saw an improvement when using this PR, e.g:
>
> 249-910-g93caec7:
> real	0m3.691s
> user	0m0.022s
> sys	0m1.338s
> 
> 249-920-g06735f2:
> real	0m2.950s
> user	0m0.005s
> sys	0m0.399s
> 
> On a different system than the one used above, I even saw a 40% improvement; results depend
> on many parameters (distro, udev rules, concurrent daemons accessing sysfs, etc.).
> 
> Because it's quite hard to measure the improvement here (as the kernel behave differently between
> the two test cases), I also ran tests using a modified kernel not hitting the trylock logic. There was
> an improvement with this PR as well. (Take this with a grain of salt though, as the kernel was
> modified not using patches approved upstream).
2021-10-11 09:40:43 +02:00
Yu Watanabe
1321f675e7 udev: read more attributes through netlink and cache them 2021-10-06 16:39:52 +09:00
Yu Watanabe
1c2ad7a66c udev: replace sd_device_get_sysattr_value() with device_get_sysattr_value_maybe_from_netlink() 2021-10-06 16:39:52 +09:00
Yu Watanabe
e44b0fbce7 udev: use LinkInfo::iftype at one more place 2021-10-06 16:39:52 +09:00
Yu Watanabe
78ae4d449e udev: cache obtained sysattr from netlink into sd_device object 2021-10-06 16:39:52 +09:00
Yu Watanabe
552b1699e2 test: add tests for device_get_sysattr_value_maybe_from_netlink() 2021-10-06 16:39:52 +09:00
Yu Watanabe
d9ea90598f udev: introduce device_get_sysattr_value_maybe_from_netlink() 2021-10-06 16:39:52 +09:00
Yu Watanabe
6ebc6a4560 udev: rename udev-builtin-net_id-netlink.[ch] 2021-10-06 16:39:52 +09:00
Yu Watanabe
914ac555cd ether-addr-util: make hw_addr_to_string() return valid string even if hardware address is null
Previously, when the length of the hardware address is zero, then the
buffer was not nul-terminated.

This also replaces sprintf() with hexchar().
2021-10-06 16:39:47 +09:00
Yu Watanabe
18b1137977 sd-device: expose device_cache_sysattr_value() and device_get_cached_sysattr_value() 2021-10-06 16:02:06 +09:00
Yu Watanabe
b63dd5aad8 sd-device: make device_get_cached_sysattr_value() distinguish if we have looked up the attribute 2021-10-06 16:00:55 +09:00
30 changed files with 1079 additions and 430 deletions

View File

@ -364,3 +364,10 @@ disk images with `--image=` or similar:
against any of the certificates in `/etc/verity.d/*.crt` (and similar
directores in `/usr/lib/`, `/run`, …) or passed to the kernel for validation
against its built-in certificates.
`systemd-cryptsetup`:
* `$SYSTEMD_CRYPTSETUP_USE_TOKEN_MODULE` takes a boolean, which controls
whether to use the libcryptsetup "token" plugin module logic even when
activating via FIDO2, PKCS#11, TPM2, i.e. mechanisms natively supported by
`systemd-cryptsetup`. Defaults to enabled.

View File

@ -677,6 +677,18 @@
of the current PCR state.</para></listitem>
</varlistentry>
<varlistentry>
<term><option>token-timeout=</option></term>
<listitem><para>Specifies how long to wait at most for configured security devices (i.e. FIDO2,
PKCS#11, TPM2) to show up. Takes a time value in seconds (but other time units may be specified too,
see <citerefentry><refentrytitle>systemd.time</refentrytitle><manvolnum>7</manvolnum></citerefentry>
for supported formats). Defaults to 30s. Once the specified timeout elapsed authentication via
password is attempted. Note that this timeout applies to waiting for the security device to show up —
it does not apply to the PIN prompt for the device (should one be needed) or similar. Pass 0 to turn
off the time-out and wait forever.</para></listitem>
</varlistentry>
<varlistentry>
<term><option>try-empty-password=</option></term>
@ -689,13 +701,9 @@
<varlistentry>
<term><option>x-systemd.device-timeout=</option></term>
<listitem><para>Specifies how long systemd should wait for a device to show up
before giving up on the entry. The argument is a time in seconds or explicitly
specified units of
<literal>s</literal>,
<literal>min</literal>,
<literal>h</literal>,
<literal>ms</literal>.
<listitem><para>Specifies how long systemd should wait for a block device to show up before
giving up on the entry. The argument is a time in seconds or explicitly specified units of
<literal>s</literal>, <literal>min</literal>, <literal>h</literal>, <literal>ms</literal>.
</para></listitem>
</varlistentry>

View File

@ -7,6 +7,7 @@
#include <sys/types.h>
#include "ether-addr-util.h"
#include "hexdecoct.h"
#include "macro.h"
#include "string-util.h"
@ -15,12 +16,13 @@ char* hw_addr_to_string(const struct hw_addr_data *addr, char buffer[HW_ADDR_TO_
assert(buffer);
assert(addr->length <= HW_ADDR_MAX_SIZE);
for (size_t i = 0; i < addr->length; i++) {
sprintf(&buffer[3*i], "%02"PRIx8, addr->bytes[i]);
if (i < addr->length - 1)
buffer[3*i + 2] = ':';
for (size_t i = 0, j = 0; i < addr->length; i++) {
buffer[j++] = hexchar(addr->bytes[i] >> 4);
buffer[j++] = hexchar(addr->bytes[i] & 0x0f);
buffer[j++] = ':';
}
buffer[addr->length > 0 ? addr->length * 3 - 1 : 0] = '\0';
return buffer;
}

View File

@ -547,12 +547,25 @@ int read_virtual_file_fd(int fd, size_t max_size, char **ret_contents, size_t *r
return !truncated;
}
int read_virtual_file(const char *filename, size_t max_size, char **ret_contents, size_t *ret_size) {
int read_virtual_file_at(
int dir_fd,
const char *filename,
size_t max_size,
char **ret_contents,
size_t *ret_size) {
_cleanup_close_ int fd = -1;
assert(filename);
assert(dir_fd >= 0 || dir_fd == AT_FDCWD);
fd = open(filename, O_RDONLY | O_NOCTTY | O_CLOEXEC);
if (!filename) {
if (dir_fd == AT_FDCWD)
return -EBADF;
return read_virtual_file_fd(dir_fd, max_size, ret_contents, ret_size);
}
fd = openat(dir_fd, filename, O_RDONLY | O_NOCTTY | O_CLOEXEC);
if (fd < 0)
return -errno;

View File

@ -69,7 +69,10 @@ static inline int read_full_file(const char *filename, char **ret_contents, size
}
int read_virtual_file_fd(int fd, size_t max_size, char **ret_contents, size_t *ret_size);
int read_virtual_file(const char *filename, size_t max_size, char **ret_contents, size_t *ret_size);
int read_virtual_file_at(int dir_fd, const char *filename, size_t max_size, char **ret_contents, size_t *ret_size);
static inline int read_virtual_file(const char *filename, size_t max_size, char **ret_contents, size_t *ret_size) {
return read_virtual_file_at(AT_FDCWD, filename, max_size, ret_contents, ret_size);
}
static inline int read_full_virtual_file(const char *filename, char **ret_contents, size_t *ret_size) {
return read_virtual_file(filename, SIZE_MAX, ret_contents, ret_size);
}

View File

@ -119,62 +119,57 @@ int on_ac_power(void) {
bool found_offline = false, found_online = false;
_cleanup_closedir_ DIR *d = NULL;
struct dirent *de;
int r;
d = opendir("/sys/class/power_supply");
if (!d)
return errno == ENOENT ? true : -errno;
FOREACH_DIRENT(de, d, return -errno) {
_cleanup_close_ int fd = -1, device = -1;
char contents[6];
ssize_t n;
_cleanup_close_ int device_fd = -1;
_cleanup_free_ char *contents = NULL;
unsigned v;
device = openat(dirfd(d), de->d_name, O_DIRECTORY|O_RDONLY|O_CLOEXEC|O_NOCTTY);
if (device < 0) {
device_fd = openat(dirfd(d), de->d_name, O_DIRECTORY|O_RDONLY|O_CLOEXEC);
if (device_fd < 0) {
if (IN_SET(errno, ENOENT, ENOTDIR))
continue;
return -errno;
}
fd = openat(device, "type", O_RDONLY|O_CLOEXEC|O_NOCTTY);
if (fd < 0) {
if (errno == ENOENT)
continue;
r = read_virtual_file_at(device_fd, "type", SIZE_MAX, &contents, NULL);
if (r == -ENOENT)
continue;
if (r < 0)
return r;
return -errno;
}
delete_trailing_chars(contents, NEWLINE);
n = read(fd, contents, sizeof(contents));
if (n < 0)
return -errno;
if (n != 6 || memcmp(contents, "Mains\n", 6))
/* We assume every power source is AC, except for batteries. See
* https://github.com/torvalds/linux/blob/4eef766b7d4d88f0b984781bc1bcb574a6eafdc7/include/linux/power_supply.h#L176
* for defined power source types. Also see:
* https://www.kernel.org/doc/Documentation/ABI/testing/sysfs-class-power */
if (streq(contents, "Battery"))
continue;
safe_close(fd);
fd = openat(device, "online", O_RDONLY|O_CLOEXEC|O_NOCTTY);
if (fd < 0) {
if (errno == ENOENT)
continue;
contents = mfree(contents);
return -errno;
}
r = read_virtual_file_at(device_fd, "online", SIZE_MAX, &contents, NULL);
if (r == -ENOENT)
continue;
if (r < 0)
return r;
n = read(fd, contents, sizeof(contents));
if (n < 0)
return -errno;
delete_trailing_chars(contents, NEWLINE);
if (n != 2 || contents[1] != '\n')
return -EIO;
if (contents[0] == '1') {
r = safe_atou(contents, &v);
if (r < 0)
return r;
if (v > 0) /* At least 1 and 2 are defined as different types of 'online' */
found_online = true;
break;
} else if (contents[0] == '0')
found_offline = true;
else
return -EIO;
found_offline = true;
}
return found_online || !found_offline;

View File

@ -276,19 +276,6 @@ static int detect_vm_dmi(void) {
#endif
}
static int detect_vm_xen(void) {
/* Check for Dom0 will be executed later in detect_vm_xen_dom0
The presence of /proc/xen indicates some form of a Xen domain */
if (access("/proc/xen", F_OK) < 0) {
log_debug("Virtualization XEN not found, /proc/xen does not exist");
return VIRTUALIZATION_NONE;
}
log_debug("Virtualization XEN found (/proc/xen exists)");
return VIRTUALIZATION_XEN;
}
#define XENFEAT_dom0 11 /* xen/include/public/features.h */
#define PATH_FEATURES "/sys/hypervisor/properties/features"
/* Returns -errno, or 0 for domU, or 1 for dom0 */
@ -342,6 +329,26 @@ static int detect_vm_xen_dom0(void) {
}
}
static int detect_vm_xen(void) {
int r;
/* The presence of /proc/xen indicates some form of a Xen domain */
if (access("/proc/xen", F_OK) < 0) {
log_debug("Virtualization XEN not found, /proc/xen does not exist");
return VIRTUALIZATION_NONE;
}
log_debug("Virtualization XEN found (/proc/xen exists)");
/* Ignore the Xen hypervisor if we are in Dom0 */
r = detect_vm_xen_dom0();
if (r < 0)
return r;
if (r > 0)
return VIRTUALIZATION_NONE;
return VIRTUALIZATION_XEN;
}
static int detect_vm_hypervisor(void) {
_cleanup_free_ char *hvtype = NULL;
int r;
@ -435,7 +442,8 @@ int detect_vm(void) {
*
* First, try to detect Oracle Virtualbox and Amazon EC2 Nitro, even if they use KVM, as well as Xen even if
* it cloaks as Microsoft Hyper-V. Attempt to detect uml at this stage also since it runs as a user-process
* nested inside other VMs.
* nested inside other VMs. Also check for Xen now, because Xen PV mode does not override CPUID when nested
* inside another hypervisor.
*
* Second, try to detect from CPUID, this will report KVM for whatever software is used even if info in DMI is
* overwritten.
@ -457,6 +465,15 @@ int detect_vm(void) {
else if (r != VIRTUALIZATION_NONE)
goto finish;
/* Detect Xen */
r = detect_vm_xen();
if (r < 0)
return r;
if (r == VIRTUALIZATION_VM_OTHER)
other = true;
else if (r != VIRTUALIZATION_NONE)
goto finish;
/* Detect from CPUID */
r = detect_vm_cpuid();
if (r < 0)
@ -476,20 +493,7 @@ int detect_vm(void) {
goto finish;
}
/* x86 xen will most likely be detected by cpuid. If not (most likely
* because we're not an x86 guest), then we should try the /proc/xen
* directory next. If that's not found, then we check for the high-level
* hypervisor sysfs file.
*/
r = detect_vm_xen();
if (r < 0)
return r;
if (r == VIRTUALIZATION_VM_OTHER)
other = true;
else if (r != VIRTUALIZATION_NONE)
goto finish;
/* Check high-level hypervisor sysfs file */
r = detect_vm_hypervisor();
if (r < 0)
return r;
@ -511,18 +515,7 @@ int detect_vm(void) {
return r;
finish:
/* x86 xen Dom0 is detected as XEN in hypervisor and maybe others.
* In order to detect the Dom0 as not virtualization we need to
* double-check it */
if (r == VIRTUALIZATION_XEN) {
int dom0;
dom0 = detect_vm_xen_dom0();
if (dom0 < 0)
return dom0;
if (dom0 > 0)
r = VIRTUALIZATION_NONE;
} else if (r == VIRTUALIZATION_NONE && other)
if (r == VIRTUALIZATION_NONE && other)
r = VIRTUALIZATION_VM_OTHER;
cached_found = r;

147
src/boot/efi/initrd.c Normal file
View File

@ -0,0 +1,147 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include <efi.h>
#include <efilib.h>
#include "initrd.h"
#include "macro-fundamental.h"
#include "missing_efi.h"
/* extend LoadFileProtocol */
struct initrd_loader {
EFI_LOAD_FILE_PROTOCOL load_file;
const VOID *address;
UINTN length;
};
/* static structure for LINUX_INITRD_MEDIA device path
see https://github.com/torvalds/linux/blob/v5.13/drivers/firmware/efi/libstub/efi-stub-helper.c
*/
static const struct {
VENDOR_DEVICE_PATH vendor;
EFI_DEVICE_PATH end;
} _packed_ efi_initrd_device_path = {
.vendor = {
.Header = {
.Type = MEDIA_DEVICE_PATH,
.SubType = MEDIA_VENDOR_DP,
.Length = { sizeof(efi_initrd_device_path.vendor), 0 }
},
.Guid = LINUX_INITRD_MEDIA_GUID
},
.end = {
.Type = END_DEVICE_PATH_TYPE,
.SubType = END_ENTIRE_DEVICE_PATH_SUBTYPE,
.Length = { sizeof(efi_initrd_device_path.end), 0 }
}
};
EFIAPI EFI_STATUS initrd_load_file(
EFI_LOAD_FILE_PROTOCOL *this,
EFI_DEVICE_PATH *file_path,
BOOLEAN boot_policy,
UINTN *buffer_size,
VOID *buffer) {
struct initrd_loader *loader;
if (!this || !buffer_size || !file_path)
return EFI_INVALID_PARAMETER;
if (boot_policy)
return EFI_UNSUPPORTED;
loader = (struct initrd_loader *) this;
if (loader->length == 0 || !loader->address)
return EFI_NOT_FOUND;
if (!buffer || *buffer_size < loader->length) {
*buffer_size = loader->length;
return EFI_BUFFER_TOO_SMALL;
}
CopyMem(buffer, loader->address, loader->length);
*buffer_size = loader->length;
return EFI_SUCCESS;
}
EFI_STATUS initrd_register(
const VOID *initrd_address,
UINTN initrd_length,
EFI_HANDLE *ret_initrd_handle) {
EFI_STATUS err;
EFI_DEVICE_PATH *dp = (EFI_DEVICE_PATH *) &efi_initrd_device_path;
EFI_HANDLE handle;
struct initrd_loader *loader;
assert(ret_initrd_handle);
if (!initrd_address || initrd_length == 0)
return EFI_SUCCESS;
/* check if a LINUX_INITRD_MEDIA_GUID DevicePath is already registed.
LocateDevicePath checks for the "closest DevicePath" and returns its handle,
where as InstallMultipleProtocolInterfaces only maches identical DevicePaths.
*/
err = uefi_call_wrapper(BS->LocateDevicePath, 3, &EfiLoadFile2Protocol, &dp, &handle);
if (err != EFI_NOT_FOUND) /* InitrdMedia is already registered */
return EFI_ALREADY_STARTED;
loader = AllocatePool(sizeof(struct initrd_loader));
if (!loader)
return EFI_OUT_OF_RESOURCES;
*loader = (struct initrd_loader) {
.load_file.LoadFile = initrd_load_file,
.address = initrd_address,
.length = initrd_length
};
/* create a new handle and register the LoadFile2 protocol with the InitrdMediaPath on it */
err = uefi_call_wrapper(
BS->InstallMultipleProtocolInterfaces, 8,
ret_initrd_handle,
&DevicePathProtocol, &efi_initrd_device_path,
&EfiLoadFile2Protocol, loader,
NULL);
if (EFI_ERROR(err))
FreePool(loader);
return err;
}
EFI_STATUS initrd_unregister(EFI_HANDLE initrd_handle) {
EFI_STATUS err;
struct initrd_loader *loader;
if (!initrd_handle)
return EFI_SUCCESS;
/* get the LoadFile2 protocol that we allocated earlier */
err = uefi_call_wrapper(
BS->OpenProtocol, 6,
initrd_handle, &EfiLoadFile2Protocol, (VOID **) &loader,
NULL, NULL, EFI_OPEN_PROTOCOL_GET_PROTOCOL);
if (EFI_ERROR(err))
return err;
/* close the handle */
(void) uefi_call_wrapper(
BS->CloseProtocol, 4,
initrd_handle, &EfiLoadFile2Protocol, NULL, NULL);
/* uninstall all protocols thus destroying the handle */
err = uefi_call_wrapper(
BS->UninstallMultipleProtocolInterfaces, 6,
initrd_handle,
&DevicePathProtocol, &efi_initrd_device_path,
&EfiLoadFile2Protocol, loader,
NULL);
if (EFI_ERROR(err))
return err;
initrd_handle = NULL;
FreePool(loader);
return EFI_SUCCESS;
}

11
src/boot/efi/initrd.h Normal file
View File

@ -0,0 +1,11 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
#include <efi.h>
EFI_STATUS initrd_register(
const VOID *initrd_address,
UINTN initrd_length,
EFI_HANDLE *ret_initrd_handle);
EFI_STATUS initrd_unregister(EFI_HANDLE initrd_handle);

View File

@ -4,6 +4,7 @@
#include <efilib.h>
#include "linux.h"
#include "initrd.h"
#include "util.h"
#ifdef __i386__
@ -28,21 +29,25 @@ static VOID linux_efi_handover(EFI_HANDLE image, struct boot_params *params) {
handover(image, ST, params);
}
EFI_STATUS linux_exec(EFI_HANDLE image,
CHAR8 *cmdline, UINTN cmdline_len,
UINTN linux_addr,
UINTN initrd_addr, UINTN initrd_size) {
EFI_STATUS linux_exec(
EFI_HANDLE image,
const CHAR8 *cmdline, UINTN cmdline_len,
const VOID *linux_buffer,
const VOID *initrd_buffer, UINTN initrd_length) {
const struct boot_params *image_params;
struct boot_params *boot_params;
EFI_HANDLE initrd_handle = NULL;
EFI_PHYSICAL_ADDRESS addr;
UINT8 setup_sectors;
EFI_STATUS err;
assert(image);
assert(cmdline);
assert(cmdline || cmdline_len == 0);
assert(linux_buffer);
assert(initrd_buffer || initrd_length == 0);
image_params = (const struct boot_params *) linux_addr;
image_params = (const struct boot_params *) linux_buffer;
if (image_params->hdr.boot_flag != 0xAA55 ||
image_params->hdr.header != SETUP_MAGIC ||
@ -65,7 +70,7 @@ EFI_STATUS linux_exec(EFI_HANDLE image,
boot_params->hdr = image_params->hdr;
boot_params->hdr.type_of_loader = 0xff;
setup_sectors = image_params->hdr.setup_sects > 0 ? image_params->hdr.setup_sects : 4;
boot_params->hdr.code32_start = (UINT32)linux_addr + (setup_sectors + 1) * 512;
boot_params->hdr.code32_start = (UINT32) POINTER_TO_PHYSICAL_ADDRESS(linux_buffer) + (setup_sectors + 1) * 512;
if (cmdline) {
addr = 0xA0000;
@ -84,9 +89,21 @@ EFI_STATUS linux_exec(EFI_HANDLE image,
boot_params->hdr.cmd_line_ptr = (UINT32) addr;
}
boot_params->hdr.ramdisk_image = (UINT32) initrd_addr;
boot_params->hdr.ramdisk_size = (UINT32) initrd_size;
/* Providing the initrd via LINUX_INITRD_MEDIA_GUID is only supported by Linux 5.8+ (5.7+ on ARM64).
Until supported kernels become more established, we continue to set ramdisk in the handover struct.
This value is overridden by kernels that support LINUX_INITRD_MEDIA_GUID.
If you need to know which protocol was used by the kernel, pass "efi=debug" to the kernel,
this will print a line when InitrdMediaGuid was successfully used to load the initrd.
*/
boot_params->hdr.ramdisk_image = (UINT32) POINTER_TO_PHYSICAL_ADDRESS(initrd_buffer);
boot_params->hdr.ramdisk_size = (UINT32) initrd_length;
/* register LINUX_INITRD_MEDIA_GUID */
err = initrd_register(initrd_buffer, initrd_length, &initrd_handle);
if (EFI_ERROR(err))
return err;
linux_efi_handover(image, boot_params);
(void) initrd_unregister(initrd_handle);
initrd_handle = NULL;
return EFI_LOAD_ERROR;
}

View File

@ -84,7 +84,8 @@ struct boot_params {
UINT8 _pad9[276];
} _packed_;
EFI_STATUS linux_exec(EFI_HANDLE image,
CHAR8 *cmdline, UINTN cmdline_size,
UINTN linux_addr,
UINTN initrd_addr, UINTN initrd_size);
EFI_STATUS linux_exec(
EFI_HANDLE image,
const CHAR8 *cmdline, UINTN cmdline_len,
const VOID *linux_buffer,
const VOID *initrd_buffer, UINTN initrd_length);

View File

@ -38,6 +38,7 @@ systemd_boot_sources = '''
stub_sources = '''
linux.c
initrd.c
splash.c
stub.c
cpio.c

View File

@ -331,3 +331,12 @@ typedef struct tdEFI_TCG2_PROTOCOL {
} EFI_TCG2;
#endif
#ifndef EFI_LOAD_FILE2_PROTOCOL_GUID
#define EFI_LOAD_FILE2_PROTOCOL_GUID \
{0x4006c0c1, 0xfcb3, 0x403e, {0x99, 0x6d, 0x4a, 0x6c, 0x87, 0x24, 0xe0, 0x6d} }
#define EfiLoadFile2Protocol ((EFI_GUID)EFI_LOAD_FILE2_PROTOCOL_GUID)
#endif
#define LINUX_INITRD_MEDIA_GUID \
{0x5568e427, 0x68fc, 0x4f3d, {0xac, 0x74, 0xca, 0x55, 0x52, 0x31, 0xcc, 0x68} }

View File

@ -249,7 +249,9 @@ EFI_STATUS efi_main(EFI_HANDLE image, EFI_SYSTEM_TABLE *sys_table) {
}
}
err = linux_exec(image, cmdline, cmdline_len, linux_base, initrd_base, initrd_size);
err = linux_exec(image, cmdline, cmdline_len,
PHYSICAL_ADDRESS_TO_POINTER(linux_base),
PHYSICAL_ADDRESS_TO_POINTER(initrd_base), initrd_size);
graphics_mode(FALSE);
return log_error_status_stall(err, L"Execution of embedded linux image failed: %r", err);
}

View File

@ -32,6 +32,7 @@ int acquire_fido2_key(
_cleanup_strv_free_erase_ char **pins = NULL;
_cleanup_free_ void *loaded_salt = NULL;
bool device_exists = false;
const char *salt;
size_t salt_size;
char *e;
@ -89,13 +90,29 @@ int acquire_fido2_key(
-ENOANO, /* needs pin */
-ENOLCK)) /* pin incorrect */
return r;
}
pins = strv_free_erase(pins);
device_exists = true; /* that a PIN is needed/wasn't correct means that we managed to
* talk to a device */
}
if (headless)
return log_error_errno(SYNTHETIC_ERRNO(ENOPKG), "PIN querying disabled via 'headless' option. Use the '$PIN' environment variable.");
if (!device_exists) {
/* Before we inquire for the PIN we'll need, if we never talked to the device, check
* if the device actually is plugged in. Otherwise we'll ask for the PIN already when
* the device is not plugged in, which is confusing. */
r = fido2_have_device(device);
if (r < 0)
return r;
if (r == 0) /* no device found, return EAGAIN so that caller will wait/watch udev */
return -EAGAIN;
device_exists = true; /* now we know for sure, a device exists, no need to ask again */
}
pins = strv_free_erase(pins);
r = ask_password_auto("Please enter security token PIN:", "drive-harddisk", NULL, "fido2-pin", "cryptsetup.fido2-pin", until, ask_password_flags, &pins);
if (r < 0)
return log_error_errno(r, "Failed to ask for user password: %m");

View File

@ -18,6 +18,7 @@
#include "cryptsetup-util.h"
#include "device-util.h"
#include "efi-loader.h"
#include "env-util.h"
#include "escape.h"
#include "fileio.h"
#include "fs-util.h"
@ -82,6 +83,7 @@ static char *arg_tpm2_device = NULL;
static bool arg_tpm2_device_auto = false;
static uint32_t arg_tpm2_pcr_mask = UINT32_MAX;
static bool arg_headless = false;
static usec_t arg_token_timeout_usec = 30*USEC_PER_SEC;
STATIC_DESTRUCTOR_REGISTER(arg_cipher, freep);
STATIC_DESTRUCTOR_REGISTER(arg_hash, freep);
@ -409,7 +411,15 @@ static int parse_one_option(const char *option) {
} else if (streq(option, "headless"))
arg_headless = true;
else if (!streq(option, "x-initrd.attach"))
else if ((val = startswith(option, "token-timeout="))) {
r = parse_sec_fix_0(val, &arg_token_timeout_usec);
if (r < 0) {
log_error_errno(r, "Failed to parse %s, ignoring: %m", option);
return 0;
}
} else if (!streq(option, "x-initrd.attach"))
log_warning("Encountered unknown /etc/crypttab option '%s', ignoring.", option);
return 0;
@ -710,11 +720,25 @@ static char *make_bindname(const char *volume) {
return s;
}
static int make_security_device_monitor(sd_event *event, sd_device_monitor **ret) {
static int make_security_device_monitor(
sd_event **ret_event,
sd_device_monitor **ret_monitor) {
_cleanup_(sd_device_monitor_unrefp) sd_device_monitor *monitor = NULL;
_cleanup_(sd_event_unrefp) sd_event *event = NULL;
int r;
assert(ret);
assert(ret_event);
assert(ret_monitor);
/* Waits for a device with "security-device" tag to show up in udev */
r = sd_event_default(&event);
if (r < 0)
return log_error_errno(r, "Failed to allocate event loop: %m");
r = sd_event_add_time_relative(event, NULL, CLOCK_MONOTONIC, arg_token_timeout_usec, USEC_PER_SEC, NULL, INT_TO_PTR(-ETIMEDOUT));
if (r < 0)
return log_error_errno(r, "Failed to install timeout event source: %m");
r = sd_device_monitor_new(&monitor);
if (r < 0)
@ -732,13 +756,61 @@ static int make_security_device_monitor(sd_event *event, sd_device_monitor **ret
if (r < 0)
return log_error_errno(r, "Failed to start device monitor: %m");
*ret = TAKE_PTR(monitor);
*ret_event = TAKE_PTR(event);
*ret_monitor = TAKE_PTR(monitor);
return 0;
}
static int run_security_device_monitor(
sd_event *event,
sd_device_monitor *monitor) {
bool processed = false;
int r;
assert(event);
assert(monitor);
/* Runs the event loop for the device monitor until either something happens, or the time-out is
* hit. */
for (;;) {
int x;
r = sd_event_get_exit_code(event, &x);
if (r < 0) {
if (r != -ENODATA)
return log_error_errno(r, "Failed to query exit code from event loop: %m");
/* On ENODATA we aren't told to exit yet. */
} else {
assert(x == -ETIMEDOUT);
return log_notice_errno(SYNTHETIC_ERRNO(EAGAIN),
"Timed out waiting for security device, aborting security device based authentication attempt.");
}
/* Wait for one event, and then eat all subsequent events until there are no further ones */
r = sd_event_run(event, processed ? 0 : UINT64_MAX);
if (r < 0)
return log_error_errno(r, "Failed to run event loop: %m");
if (r == 0) /* no events queued anymore */
return 0;
processed = true;
}
}
static bool libcryptsetup_plugins_support(void) {
#if HAVE_LIBCRYPTSETUP_PLUGINS
return crypt_token_external_path() != NULL;
int r;
/* Permit a way to disable libcryptsetup token module support, for debugging purposes. */
r = getenv_bool("SYSTEMD_CRYPTSETUP_USE_TOKEN_MODULE");
if (r < 0 && r != -ENXIO)
log_debug_errno(r, "Failed to parse $SYSTEMD_CRYPTSETUP_USE_TOKEN_MODULE env var: %m");
if (r == 0)
return false;
return crypt_token_external_path();
#else
return false;
#endif
@ -776,11 +848,11 @@ static int attach_luks2_by_fido2(
void *usrptr,
uint32_t activation_flags) {
int r = -EOPNOTSUPP;
#if HAVE_LIBCRYPTSETUP_PLUGINS
char **p;
_cleanup_strv_free_erase_ char **pins = NULL;
AskPasswordFlags flags = ASK_PASSWORD_PUSH_CACHE | ASK_PASSWORD_ACCEPT_CACHED;
_cleanup_strv_free_erase_ char **pins = NULL;
char **p;
int r;
r = crypt_activate_by_token_pin(cd, name, "systemd-fido2", CRYPT_ANY_TOKEN, NULL, 0, usrptr, activation_flags);
if (r > 0) /* returns unlocked keyslot id on success */
@ -803,20 +875,6 @@ static int attach_luks2_by_fido2(
if (headless)
return log_error_errno(SYNTHETIC_ERRNO(ENOPKG), "PIN querying disabled via 'headless' option. Use the '$PIN' environment variable.");
pins = strv_free_erase(pins);
r = ask_password_auto("Please enter security token PIN:", "drive-harddisk", NULL, "fido2-pin", "cryptsetup.fido2-pin", until, flags, &pins);
if (r < 0)
return r;
STRV_FOREACH(p, pins) {
r = crypt_activate_by_token_pin(cd, name, "systemd-fido2", CRYPT_ANY_TOKEN, *p, strlen(*p), usrptr, activation_flags);
if (r > 0) /* returns unlocked keyslot id on success */
r = 0;
if (r != -ENOANO) /* needs pin or pin is wrong */
return r;
}
flags &= ~ASK_PASSWORD_ACCEPT_CACHED;
for (;;) {
pins = strv_free_erase(pins);
r = ask_password_auto("Please enter security token PIN:", "drive-harddisk", NULL, "fido2-pin", "cryptsetup.fido2-pin", until, flags, &pins);
@ -830,9 +888,13 @@ static int attach_luks2_by_fido2(
if (r != -ENOANO) /* needs pin or pin is wrong */
return r;
}
flags &= ~ASK_PASSWORD_ACCEPT_CACHED;
}
#endif
return r;
#else
return -EOPNOTSUPP;
#endif
}
static int attach_luks_or_plain_or_bitlk_by_fido2(
@ -908,8 +970,6 @@ static int attach_luks_or_plain_or_bitlk_by_fido2(
return log_oom();
for (;;) {
bool processed = false;
if (use_libcryptsetup_plugin && !arg_fido2_cid) {
r = attach_luks2_by_fido2(cd, name, until, arg_headless, arg_fido2_device, flags);
if (IN_SET(r, -ENOTUNIQ, -ENXIO, -ENOENT))
@ -943,11 +1003,7 @@ static int attach_luks_or_plain_or_bitlk_by_fido2(
assert(!event);
r = sd_event_default(&event);
if (r < 0)
return log_error_errno(r, "Failed to allocate event loop: %m");
r = make_security_device_monitor(event, &monitor);
r = make_security_device_monitor(&event, &monitor);
if (r < 0)
return r;
@ -958,17 +1014,9 @@ static int attach_luks_or_plain_or_bitlk_by_fido2(
continue;
}
for (;;) {
/* Wait for one event, and then eat all subsequent events until there are no
* further ones */
r = sd_event_run(event, processed ? 0 : UINT64_MAX);
if (r < 0)
return log_error_errno(r, "Failed to run event loop: %m");
if (r == 0)
break;
processed = true;
}
r = run_security_device_monitor(event, monitor);
if (r < 0)
return r;
log_debug("Got one or more potentially relevant udev events, rescanning FIDO2...");
}
@ -1004,9 +1052,10 @@ static int attach_luks2_by_pkcs11(
bool headless,
uint32_t flags) {
int r = -EOPNOTSUPP;
#if HAVE_LIBCRYPTSETUP_PLUGINS
if (!crypt_get_type(cd) || strcmp(crypt_get_type(cd), CRYPT_LUKS2))
int r;
if (!streq_ptr(crypt_get_type(cd), CRYPT_LUKS2))
return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Automatic PKCS#11 metadata requires LUKS2 device.");
systemd_pkcs11_plugin_params params = {
@ -1018,8 +1067,11 @@ static int attach_luks2_by_pkcs11(
r = crypt_activate_by_token_pin(cd, name, "systemd-pkcs11", CRYPT_ANY_TOKEN, NULL, 0, &params, flags);
if (r > 0) /* returns unlocked keyslot id on success */
r = 0;
#endif
return r;
#else
return -EOPNOTSUPP;
#endif
}
static int attach_luks_or_plain_or_bitlk_by_pkcs11(
@ -1071,8 +1123,6 @@ static int attach_luks_or_plain_or_bitlk_by_pkcs11(
return log_oom();
for (;;) {
bool processed = false;
if (use_libcryptsetup_plugin && arg_pkcs11_uri_auto)
r = attach_luks2_by_pkcs11(cd, name, friendly, until, arg_headless, flags);
else {
@ -1098,11 +1148,7 @@ static int attach_luks_or_plain_or_bitlk_by_pkcs11(
assert(!event);
r = sd_event_default(&event);
if (r < 0)
return log_error_errno(r, "Failed to allocate event loop: %m");
r = make_security_device_monitor(event, &monitor);
r = make_security_device_monitor(&event, &monitor);
if (r < 0)
return r;
@ -1114,17 +1160,9 @@ static int attach_luks_or_plain_or_bitlk_by_pkcs11(
continue;
}
for (;;) {
/* Wait for one event, and then eat all subsequent events until there are no
* further ones */
r = sd_event_run(event, processed ? 0 : UINT64_MAX);
if (r < 0)
return log_error_errno(r, "Failed to run event loop: %m");
if (r == 0)
break;
processed = true;
}
r = run_security_device_monitor(event, monitor);
if (r < 0)
return r;
log_debug("Got one or more potentially relevant udev events, rescanning PKCS#11...");
}
@ -1159,11 +1197,24 @@ static int attach_luks_or_plain_or_bitlk_by_pkcs11(
return 0;
}
static int make_tpm2_device_monitor(sd_event *event, sd_device_monitor **ret) {
static int make_tpm2_device_monitor(
sd_event **ret_event,
sd_device_monitor **ret_monitor) {
_cleanup_(sd_device_monitor_unrefp) sd_device_monitor *monitor = NULL;
_cleanup_(sd_event_unrefp) sd_event *event = NULL;
int r;
assert(ret);
assert(ret_event);
assert(ret_monitor);
r = sd_event_default(&event);
if (r < 0)
return log_error_errno(r, "Failed to allocate event loop: %m");
r = sd_event_add_time_relative(event, NULL, CLOCK_MONOTONIC, arg_token_timeout_usec, USEC_PER_SEC, NULL, INT_TO_PTR(-ETIMEDOUT));
if (r < 0)
return log_error_errno(r, "Failed to install timeout event source: %m");
r = sd_device_monitor_new(&monitor);
if (r < 0)
@ -1181,7 +1232,8 @@ static int make_tpm2_device_monitor(sd_event *event, sd_device_monitor **ret) {
if (r < 0)
return log_error_errno(r, "Failed to start device monitor: %m");
*ret = TAKE_PTR(monitor);
*ret_event = TAKE_PTR(event);
*ret_monitor = TAKE_PTR(monitor);
return 0;
}
@ -1238,8 +1290,6 @@ static int attach_luks_or_plain_or_bitlk_by_tpm2(
return log_oom();
for (;;) {
bool processed = false;
if (key_file || key_data) {
/* If key data is specified, use that */
@ -1343,11 +1393,7 @@ static int attach_luks_or_plain_or_bitlk_by_tpm2(
return log_notice_errno(SYNTHETIC_ERRNO(EAGAIN),
"No TPM2 hardware discovered and EFI bios indicates no support for it either, assuming TPM2-less system, falling back to traditional unocking.");
r = sd_event_default(&event);
if (r < 0)
return log_error_errno(r, "Failed to allocate event loop: %m");
r = make_tpm2_device_monitor(event, &monitor);
r = make_tpm2_device_monitor(&event, &monitor);
if (r < 0)
return r;
@ -1358,17 +1404,9 @@ static int attach_luks_or_plain_or_bitlk_by_tpm2(
continue;
}
for (;;) {
/* Wait for one event, and then eat all subsequent events until there are no
* further ones */
r = sd_event_run(event, processed ? 0 : UINT64_MAX);
if (r < 0)
return log_error_errno(r, "Failed to run event loop: %m");
if (r == 0)
break;
processed = true;
}
r = run_security_device_monitor(event, monitor);
if (r < 0)
return r;
log_debug("Got one or more potentially relevant udev events, rescanning for TPM2...");
}

View File

@ -18,13 +18,15 @@ static inline int device_new_from_watch_handle(sd_device **ret, int wd) {
}
int device_get_device_id(sd_device *device, const char **ret);
int device_get_devlink_priority(sd_device *device, int *priority);
int device_get_watch_handle(sd_device *device);
int device_get_devnode_mode(sd_device *device, mode_t *mode);
int device_get_devnode_uid(sd_device *device, uid_t *uid);
int device_get_devnode_gid(sd_device *device, gid_t *gid);
int device_cache_sysattr_value(sd_device *device, const char *key, char *value);
int device_get_cached_sysattr_value(sd_device *device, const char *key, const char **ret_value);
void device_seal(sd_device *device);
void device_set_is_initialized(sd_device *device);
int device_set_watch_handle(sd_device *device, int wd);

View File

@ -1941,7 +1941,7 @@ _public_ int sd_device_get_trigger_uuid(sd_device *device, sd_id128_t *ret) {
return 0;
}
static int device_cache_sysattr_value(sd_device *device, const char *key, char *value) {
int device_cache_sysattr_value(sd_device *device, const char *key, char *value) {
_unused_ _cleanup_free_ char *old_value = NULL;
_cleanup_free_ char *new_key = NULL;
int r;
@ -1969,18 +1969,19 @@ static int device_cache_sysattr_value(sd_device *device, const char *key, char *
return 0;
}
static int device_get_cached_sysattr_value(sd_device *device, const char *_key, const char **_value) {
const char *key = NULL, *value;
int device_get_cached_sysattr_value(sd_device *device, const char *key, const char **ret_value) {
const char *k = NULL, *value;
assert(device);
assert(_key);
assert(key);
value = hashmap_get2(device->sysattr_values, _key, (void **) &key);
if (!key)
return -ENOENT;
if (_value)
*_value = value;
value = hashmap_get2(device->sysattr_values, key, (void **) &k);
if (!k)
return -ESTALE; /* We have not read the attribute. */
if (!value)
return -ENOENT; /* We have looked up the attribute before and it did not exist. */
if (ret_value)
*ret_value = value;
return 0;
}
@ -1988,7 +1989,7 @@ static int device_get_cached_sysattr_value(sd_device *device, const char *_key,
* with a NULL value in the cache, otherwise the returned string is stored */
_public_ int sd_device_get_sysattr_value(sd_device *device, const char *sysattr, const char **ret_value) {
_cleanup_free_ char *value = NULL;
const char *path, *syspath, *cached_value = NULL;
const char *path, *syspath;
struct stat statbuf;
int r;
@ -1996,20 +1997,9 @@ _public_ int sd_device_get_sysattr_value(sd_device *device, const char *sysattr,
assert_return(sysattr, -EINVAL);
/* look for possibly already cached result */
r = device_get_cached_sysattr_value(device, sysattr, &cached_value);
if (r != -ENOENT) {
if (r < 0)
return r;
if (!cached_value)
/* we looked up the sysattr before and it did not exist */
return -ENOENT;
if (ret_value)
*ret_value = cached_value;
return 0;
}
r = device_get_cached_sysattr_value(device, sysattr, ret_value);
if (r != -ESTALE)
return r;
r = sd_device_get_syspath(device, &syspath);
if (r < 0)

View File

@ -12,6 +12,7 @@
#include "memory-util.h"
#include "random-util.h"
#include "strv.h"
#include "unistd.h"
static void *libfido2_dl = NULL;
@ -1077,3 +1078,52 @@ finish:
"FIDO2 tokens not supported on this build.");
#endif
}
int fido2_have_device(const char *device) {
#if HAVE_LIBFIDO2
size_t allocated = 64, found = 0;
fido_dev_info_t *di = NULL;
int r;
/* Return == 0 if not devices are found, > 0 if at least one is found */
r = dlopen_libfido2();
if (r < 0)
return log_error_errno(r, "FIDO2 support is not installed.");
if (device) {
if (access(device, F_OK) < 0) {
if (errno == ENOENT)
return 0;
return log_error_errno(errno, "Failed to determine whether device '%s' exists: %m", device);
}
return 1;
}
di = sym_fido_dev_info_new(allocated);
if (!di)
return log_oom();
r = sym_fido_dev_info_manifest(di, allocated, &found);
if (r == FIDO_ERR_INTERNAL) {
/* The library returns FIDO_ERR_INTERNAL when no devices are found. I wish it wouldn't. */
r = 0;
goto finish;
}
if (r != FIDO_OK) {
r = log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to enumerate FIDO2 devices: %s", sym_fido_strerr(r));
goto finish;
}
r = found;
finish:
sym_fido_dev_info_free(&di, allocated);
return r;
#else
return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
"FIDO2 tokens not supported on this build.");
#endif
}

View File

@ -119,3 +119,5 @@ int fido2_generate_hmac_hash(
int fido2_list_devices(void);
int fido2_find_device_auto(char **ret);
int fido2_have_device(const char *device);

View File

@ -33,12 +33,12 @@ libudevd_core_sources = '''
udev-builtin-hwdb.c
udev-builtin-input_id.c
udev-builtin-keyboard.c
udev-builtin-net_id-netlink.c
udev-builtin-net_id-netlink.h
udev-builtin-net_id.c
udev-builtin-net_setup_link.c
udev-builtin-path_id.c
udev-builtin-usb_id.c
udev-netlink.c
udev-netlink.h
net/link-config.c
net/link-config.h
'''.split()
@ -212,9 +212,9 @@ tests += [
[threads,
libacl]],
[['src/udev/test-udev-builtin-net_id-netlink.c',
'src/udev/udev-builtin-net_id-netlink.c',
'src/udev/udev-builtin-net_id-netlink.h']],
[['src/udev/test-udev-netlink.c',
'src/udev/udev-netlink.c',
'src/udev/udev-netlink.h']],
[['src/udev/fido_id/test-fido-id-desc.c',
'src/udev/fido_id/fido_id_desc.c']],

View File

@ -1,85 +0,0 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include "sd-device.h"
#include "arphrd-list.h"
#include "ether-addr-util.h"
#include "parse-util.h"
#include "tests.h"
#include "udev-builtin-net_id-netlink.h"
static void test_link_info_one(sd_netlink *rtnl, int ifindex) {
_cleanup_(link_info_clear) LinkInfo info = LINK_INFO_NULL;
_cleanup_(sd_device_unrefp) sd_device *dev = NULL;
unsigned iftype, iflink;
const char *s;
log_debug("/* %s(ifindex=%i) */", __func__, ifindex);
assert_se(link_info_get(&rtnl, ifindex, &info) >= 0);
assert_se(sd_device_new_from_ifindex(&dev, ifindex) >= 0);
/* check iftype */
log_debug("iftype: %"PRIu16" (%s)", info.iftype, strna(arphrd_to_name(info.iftype)));
assert_se(sd_device_get_sysattr_value(dev, "type", &s) >= 0);
assert_se(safe_atou(s, &iftype) >= 0);
assert_se(iftype == info.iftype);
/* check hardware address */
log_debug("hardware address: %s", HW_ADDR_TO_STR(&info.hw_addr));
assert_se(sd_device_get_sysattr_value(dev, "address", &s) >= 0);
assert_se(streq(s, HW_ADDR_TO_STR(&info.hw_addr)));
/* check ifname */
log_debug("ifname: %s", info.ifname);
assert_se(sd_device_get_sysname(dev, &s) >= 0);
assert_se(streq(s, info.ifname));
/* check iflink */
log_debug("iflink: %"PRIu32, info.iflink);
assert_se(sd_device_get_sysattr_value(dev, "iflink", &s) >= 0);
assert_se(safe_atou(s, &iflink) >= 0);
assert_se(iflink == info.iflink);
/* check phys_port_name */
log_debug("phys_port_name: %s (%s)",
strna(info.phys_port_name),
info.support_phys_port_name ? "supported" : "unsupported");
if (info.support_phys_port_name) {
s = NULL;
(void) sd_device_get_sysattr_value(dev, "phys_port_name", &s);
assert_se(streq_ptr(s, info.phys_port_name));
}
}
static void test_link_info_get(void) {
_cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL;
log_debug("/* %s */", __func__);
assert_se(sd_netlink_open(&rtnl) >= 0);
assert_se(sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, 0) >= 0);
assert_se(sd_netlink_message_request_dump(req, true) >= 0);
assert_se(sd_netlink_call(rtnl, req, 0, &reply) >= 0);
for (sd_netlink_message *reply_one = reply; reply_one; reply_one = sd_netlink_message_next(reply_one)) {
uint16_t nlmsg_type;
int ifindex;
assert_se(sd_netlink_message_get_type(reply_one, &nlmsg_type) >= 0);
assert_se(nlmsg_type == RTM_NEWLINK);
assert_se(sd_rtnl_message_link_get_ifindex(reply_one, &ifindex) >= 0);
test_link_info_one(rtnl, ifindex);
}
}
int main(int argc, char *argv[]) {
test_setup_logging(LOG_DEBUG);
test_link_info_get();
return 0;
}

View File

@ -0,0 +1,165 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include "sd-device.h"
#include "arphrd-list.h"
#include "ether-addr-util.h"
#include "parse-util.h"
#include "tests.h"
#include "udev-netlink.h"
static void test_link_info_one(sd_netlink *rtnl, int ifindex) {
_cleanup_(link_info_clear) LinkInfo info = LINK_INFO_NULL;
_cleanup_(sd_device_unrefp) sd_device *dev = NULL, *dev_with_netlink = NULL;
const char *s, *t;
unsigned u;
log_debug("/* %s(ifindex=%i) */", __func__, ifindex);
assert_se(link_info_get(&rtnl, ifindex, &info) >= 0);
assert_se(sd_device_new_from_ifindex(&dev, ifindex) >= 0);
assert_se(sd_device_new_from_ifindex(&dev_with_netlink, ifindex) >= 0);
/* check iftype */
log_debug("iftype: %"PRIu16" (%s)", info.iftype, strna(arphrd_to_name(info.iftype)));
assert_se(sd_device_get_sysattr_value(dev, "type", &s) >= 0);
assert_se(safe_atou(s, &u) >= 0);
assert_se(u == info.iftype);
assert_se(device_get_sysattr_value_maybe_from_netlink(dev_with_netlink, &rtnl, "type", &s) >= 0);
assert_se(safe_atou(s, &u) >= 0);
assert_se(u == info.iftype);
/* check hardware address length */
log_debug("hardware address length: %zu", info.hw_addr.length);
assert_se(sd_device_get_sysattr_value(dev, "addr_len", &s) >= 0);
assert_se(safe_atou(s, &u) >= 0);
assert_se(u == info.hw_addr.length);
assert_se(device_get_sysattr_value_maybe_from_netlink(dev_with_netlink, &rtnl, "addr_len", &s) >= 0);
assert_se(safe_atou(s, &u) >= 0);
assert_se(u == info.hw_addr.length);
/* check hardware address */
log_debug("hardware address: %s", HW_ADDR_TO_STR(&info.hw_addr));
assert_se(sd_device_get_sysattr_value(dev, "address", &s) >= 0);
assert_se(streq(s, HW_ADDR_TO_STR(&info.hw_addr)));
assert_se(device_get_sysattr_value_maybe_from_netlink(dev_with_netlink, &rtnl, "address", &s) >= 0);
assert_se(streq(s, HW_ADDR_TO_STR(&info.hw_addr)));
/* check broadcast address */
log_debug("broadcast address: %s", HW_ADDR_TO_STR(&info.broadcast));
assert_se(sd_device_get_sysattr_value(dev, "broadcast", &s) >= 0);
assert_se(streq(s, HW_ADDR_TO_STR(&info.broadcast)));
assert_se(device_get_sysattr_value_maybe_from_netlink(dev_with_netlink, &rtnl, "broadcast", &s) >= 0);
assert_se(streq(s, HW_ADDR_TO_STR(&info.broadcast)));
/* check ifname */
log_debug("ifname: %s", info.ifname);
assert_se(sd_device_get_sysname(dev, &s) >= 0);
assert_se(streq(s, info.ifname));
assert_se(sd_device_get_sysname(dev_with_netlink, &s) >= 0);
assert_se(streq(s, info.ifname));
/* check mtu */
log_debug("mtu: %"PRIu32, info.mtu);
assert_se(sd_device_get_sysattr_value(dev, "mtu", &s) >= 0);
assert_se(safe_atou(s, &u) >= 0);
assert_se(u == info.mtu);
assert_se(device_get_sysattr_value_maybe_from_netlink(dev_with_netlink, &rtnl, "mtu", &s) >= 0);
assert_se(safe_atou(s, &u) >= 0);
assert_se(u == info.mtu);
/* check iflink */
log_debug("iflink: %"PRIu32, info.iflink);
assert_se(sd_device_get_sysattr_value(dev, "iflink", &s) >= 0);
assert_se(safe_atou(s, &u) >= 0);
assert_se(u == info.iflink);
assert_se(device_get_sysattr_value_maybe_from_netlink(dev_with_netlink, &rtnl, "iflink", &s) >= 0);
assert_se(safe_atou(s, &u) >= 0);
assert_se(u == info.iflink);
/* check link_mode */
log_debug("link_mode: %"PRIu8, info.link_mode);
assert_se(sd_device_get_sysattr_value(dev, "link_mode", &s) >= 0);
assert_se(safe_atou(s, &u) >= 0);
assert_se(u == info.link_mode);
assert_se(device_get_sysattr_value_maybe_from_netlink(dev_with_netlink, &rtnl, "link_mode", &s) >= 0);
assert_se(safe_atou(s, &u) >= 0);
assert_se(u == info.link_mode);
/* check ifalias */
log_debug("ifalias: %s", strna(info.ifalias));
assert_se(sd_device_get_sysattr_value(dev, "ifalias", &s) >= 0);
assert_se(streq(s, strempty(info.ifalias)));
assert_se(device_get_sysattr_value_maybe_from_netlink(dev_with_netlink, &rtnl, "ifalias", &s) >= 0);
assert_se(streq(s, strempty(info.ifalias)));
/* check netdev_group */
log_debug("netdev_group: %"PRIu32, info.group);
assert_se(sd_device_get_sysattr_value(dev, "netdev_group", &s) >= 0);
assert_se(safe_atou(s, &u) >= 0);
assert_se(u == info.group);
assert_se(device_get_sysattr_value_maybe_from_netlink(dev_with_netlink, &rtnl, "netdev_group", &s) >= 0);
assert_se(safe_atou(s, &u) >= 0);
assert_se(u == info.group);
/* check phys_port_id */
log_debug("phys_port_id: (%s)",
info.phys_port_id_supported ? "supported" : "unsupported");
s = t = NULL;
(void) sd_device_get_sysattr_value(dev, "phys_port_id", &s);
(void) device_get_sysattr_value_maybe_from_netlink(dev_with_netlink, &rtnl, "phys_port_id", &t);
assert_se(streq_ptr(s, t));
/* check phys_switch_id */
log_debug("phys_switch_id: (%s)",
info.phys_switch_id_supported ? "supported" : "unsupported");
s = t = NULL;
(void) sd_device_get_sysattr_value(dev, "phys_switch_id", &s);
(void) device_get_sysattr_value_maybe_from_netlink(dev_with_netlink, &rtnl, "phys_switch_id", &t);
assert_se(streq_ptr(s, t));
/* check phys_port_name */
log_debug("phys_port_name: %s (%s)",
strna(info.phys_port_name),
info.phys_port_name_supported ? "supported" : "unsupported");
s = t = NULL;
(void) sd_device_get_sysattr_value(dev, "phys_port_name", &s);
(void) device_get_sysattr_value_maybe_from_netlink(dev_with_netlink, &rtnl, "phys_port_name", &t);
assert_se(streq_ptr(s, t));
if (info.phys_port_name_supported) {
assert_se(streq_ptr(s, info.phys_port_name));
assert_se(streq_ptr(t, info.phys_port_name));
}
}
static void test_link_info_get(void) {
_cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL, *reply = NULL;
log_debug("/* %s */", __func__);
assert_se(sd_netlink_open(&rtnl) >= 0);
assert_se(sd_rtnl_message_new_link(rtnl, &req, RTM_GETLINK, 0) >= 0);
assert_se(sd_netlink_message_request_dump(req, true) >= 0);
assert_se(sd_netlink_call(rtnl, req, 0, &reply) >= 0);
for (sd_netlink_message *reply_one = reply; reply_one; reply_one = sd_netlink_message_next(reply_one)) {
uint16_t nlmsg_type;
int ifindex;
assert_se(sd_netlink_message_get_type(reply_one, &nlmsg_type) >= 0);
assert_se(nlmsg_type == RTM_NEWLINK);
assert_se(sd_rtnl_message_link_get_ifindex(reply_one, &ifindex) >= 0);
test_link_info_one(rtnl, ifindex);
}
}
int main(int argc, char *argv[]) {
test_setup_logging(LOG_DEBUG);
test_link_info_get();
return 0;
}

View File

@ -1,87 +0,0 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include "netlink-util.h"
#include "udev-builtin-net_id-netlink.h"
void link_info_clear(LinkInfo *info) {
if (!info)
return;
info->ifname = mfree(info->ifname);
info->phys_port_name = mfree(info->phys_port_name);
}
int link_info_get(sd_netlink **rtnl, int ifindex, LinkInfo *ret) {
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *message = NULL, *reply = NULL;
_cleanup_(link_info_clear) LinkInfo info = LINK_INFO_NULL;
uint16_t nlmsg_type;
int r;
assert(rtnl);
assert(ifindex > 0);
assert(ret);
if (!*rtnl) {
r = sd_netlink_open(rtnl);
if (r < 0)
return r;
}
r = sd_rtnl_message_new_link(*rtnl, &message, RTM_GETLINK, ifindex);
if (r < 0)
return r;
r = sd_netlink_call(*rtnl, message, 0, &reply);
if (r == -EINVAL)
return -ENODEV; /* The device does not exist */
if (r < 0)
return r;
r = sd_netlink_message_get_type(reply, &nlmsg_type);
if (r < 0)
return r;
if (nlmsg_type != RTM_NEWLINK)
return -ENXIO;
r = sd_rtnl_message_link_get_ifindex(reply, &info.ifindex);
if (r < 0)
return r;
if (info.ifindex != ifindex)
return -ENXIO;
r = sd_rtnl_message_link_get_type(reply, &info.iftype);
if (r < 0)
return r;
r = netlink_message_read_hw_addr(reply, IFLA_ADDRESS, &info.hw_addr);
if (r < 0 && r != -ENODATA)
return r;
r = sd_netlink_message_read_string_strdup(reply, IFLA_IFNAME, &info.ifname);
if (r < 0)
return r;
r = sd_netlink_message_read_u32(reply, IFLA_LINK, &info.iflink);
if (r == -ENODATA)
info.iflink = info.ifindex;
else if (r < 0)
return r;
r = sd_netlink_message_read_string_strdup(reply, IFLA_PHYS_PORT_NAME, &info.phys_port_name);
if (r == -ENODATA) {
uint16_t max_attr;
r = sd_netlink_message_get_max_attribute(reply, &max_attr);
if (r < 0)
return r;
info.support_phys_port_name = max_attr >= IFLA_PHYS_PORT_NAME;
} else if (r >= 0)
info.support_phys_port_name = true;
else
return r;
*ret = info;
info = LINK_INFO_NULL;
return 0;
}

View File

@ -1,23 +0,0 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
#include "sd-netlink.h"
#include "ether-addr-util.h"
typedef struct LinkInfo {
int ifindex;
uint16_t iftype; /* ARPHRD_* */
struct hw_addr_data hw_addr; /* IFLA_ADDRESS */
char *ifname; /* IFLA_IFNAME */
uint32_t iflink; /* IFLA_LINK */
char *phys_port_name; /* IFLA_PHYS_PORT_NAME */
bool support_phys_port_name;
} LinkInfo;
#define LINK_INFO_NULL ((LinkInfo) {})
void link_info_clear(LinkInfo *info);
int link_info_get(sd_netlink **rtnl, int ifindex, LinkInfo *ret);

View File

@ -35,8 +35,8 @@
#include "string-util.h"
#include "strv.h"
#include "strxcpyx.h"
#include "udev-builtin-net_id-netlink.h"
#include "udev-builtin.h"
#include "udev-netlink.h"
#define ONBOARD_14BIT_INDEX_MAX ((1U << 14) - 1)
#define ONBOARD_16BIT_INDEX_MAX ((1U << 16) - 1)
@ -342,22 +342,16 @@ static int dev_pci_slot(sd_device *dev, const LinkInfo *info, NetNames *names) {
r = safe_atolu_full(attr, 10, &dev_port);
if (r < 0)
log_device_debug_errno(dev, r, "Failed to parse attribute dev_port, ignoring: %m");
/* With older kernels IP-over-InfiniBand network interfaces sometimes erroneously
* provide the port number in the 'dev_id' sysfs attribute instead of 'dev_port',
* which thus stays initialized as 0. */
if (dev_port == 0 &&
sd_device_get_sysattr_value(dev, "type", &attr) >= 0) {
unsigned long type;
r = safe_atolu_full(attr, 10, &type);
info->iftype == ARPHRD_INFINIBAND &&
sd_device_get_sysattr_value(dev, "dev_id", &attr) >= 0) {
r = safe_atolu_full(attr, 10, &dev_port);
if (r < 0)
log_device_debug_errno(dev, r, "Failed to parse attribute type, ignoring: %m");
else if (type == ARPHRD_INFINIBAND &&
sd_device_get_sysattr_value(dev, "dev_id", &attr) >= 0) {
r = safe_atolu_full(attr, 10, &dev_port);
if (r < 0)
log_device_debug_errno(dev, r, "Failed to parse attribute dev_id, ignoring: %m");
}
log_device_debug_errno(dev, r, "Failed to parse attribute dev_id, ignoring: %m");
}
}
@ -859,7 +853,7 @@ static int builtin_net_id(sd_device *dev, sd_netlink **rtnl, int argc, char *arg
if (r < 0)
return r;
if (!info.support_phys_port_name) {
if (!info.phys_port_name_supported) {
const char *s;
r = sd_device_get_sysattr_value(dev, "phys_port_name", &s);
@ -870,6 +864,10 @@ static int builtin_net_id(sd_device *dev, sd_netlink **rtnl, int argc, char *arg
}
}
r = device_cache_sysattr_from_link_info(dev, &info);
if (r < 0)
return r;
/* skip stacked devices, like VLANs, ... */
if (info.ifindex != (int) info.iflink)
return 0;

View File

@ -30,6 +30,7 @@
#include "strxcpyx.h"
#include "udev-builtin.h"
#include "udev-event.h"
#include "udev-netlink.h"
#include "udev-node.h"
#include "udev-util.h"
#include "udev-watch.h"
@ -351,11 +352,11 @@ static ssize_t udev_event_subst_format(
/* try to read the attribute the device */
if (!val)
(void) sd_device_get_sysattr_value(dev, attr, &val);
(void) device_get_sysattr_value_maybe_from_netlink(dev, &event->rtnl, attr, &val);
/* try to read the attribute of the parent device, other matches have selected */
if (!val && event->dev_parent && event->dev_parent != dev)
(void) sd_device_get_sysattr_value(event->dev_parent, attr, &val);
(void) device_get_sysattr_value_maybe_from_netlink(event->dev_parent, &event->rtnl, attr, &val);
if (!val)
goto null_terminate;

330
src/udev/udev-netlink.c Normal file
View File

@ -0,0 +1,330 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#include "device-private.h"
#include "hexdecoct.h"
#include "netlink-util.h"
#include "strv.h"
#include "udev-netlink.h"
void link_info_clear(LinkInfo *info) {
if (!info)
return;
info->ifname = mfree(info->ifname);
info->ifalias = mfree(info->ifalias);
info->phys_port_id = mfree(info->phys_port_id);
info->phys_switch_id = mfree(info->phys_switch_id);
info->phys_port_name = mfree(info->phys_port_name);
}
int link_info_get(sd_netlink **rtnl, int ifindex, LinkInfo *ret) {
_cleanup_(sd_netlink_message_unrefp) sd_netlink_message *message = NULL, *reply = NULL;
_cleanup_(link_info_clear) LinkInfo info = LINK_INFO_NULL;
uint16_t nlmsg_type, max_attr;
int r;
assert(rtnl);
assert(ifindex > 0);
assert(ret);
if (!*rtnl) {
r = sd_netlink_open(rtnl);
if (r < 0)
return r;
}
r = sd_rtnl_message_new_link(*rtnl, &message, RTM_GETLINK, ifindex);
if (r < 0)
return r;
r = sd_netlink_call(*rtnl, message, 0, &reply);
if (r == -EINVAL)
return -ENODEV; /* The device does not exist */
if (r < 0)
return r;
r = sd_netlink_message_get_type(reply, &nlmsg_type);
if (r < 0)
return r;
if (nlmsg_type != RTM_NEWLINK)
return -ENXIO;
r = sd_rtnl_message_link_get_ifindex(reply, &info.ifindex);
if (r < 0)
return r;
if (info.ifindex != ifindex)
return -ENXIO;
r = sd_rtnl_message_link_get_type(reply, &info.iftype);
if (r < 0)
return r;
r = netlink_message_read_hw_addr(reply, IFLA_ADDRESS, &info.hw_addr);
if (r < 0 && r != -ENODATA)
return r;
r = netlink_message_read_hw_addr(reply, IFLA_BROADCAST, &info.broadcast);
if (r < 0 && r != -ENODATA)
return r;
r = sd_netlink_message_read_string_strdup(reply, IFLA_IFNAME, &info.ifname);
if (r < 0)
return r;
r = sd_netlink_message_read_u32(reply, IFLA_MTU, &info.mtu);
if (r < 0)
return r;
r = sd_netlink_message_read_u32(reply, IFLA_LINK, &info.iflink);
if (r == -ENODATA)
info.iflink = info.ifindex;
else if (r < 0)
return r;
r = sd_netlink_message_read_u8(reply, IFLA_LINKMODE, &info.link_mode);
if (r < 0)
return r;
r = sd_netlink_message_read_string_strdup(reply, IFLA_IFALIAS, &info.ifalias);
if (r < 0 && r != -ENODATA)
return r;
r = sd_netlink_message_read_u32(reply, IFLA_GROUP, &info.group);
if (r < 0)
return r;
r = sd_netlink_message_read_data(reply, IFLA_PHYS_PORT_ID,
&info.phys_port_id_len, (void**) &info.phys_port_id);
if (r < 0 && r != -ENODATA)
return r;
r = sd_netlink_message_read_data(reply, IFLA_PHYS_SWITCH_ID,
&info.phys_switch_id_len, (void**) &info.phys_switch_id);
if (r < 0 && r != -ENODATA)
return r;
r = sd_netlink_message_read_string_strdup(reply, IFLA_PHYS_PORT_NAME, &info.phys_port_name);
if (r < 0 && r != -ENODATA)
return r;
r = sd_netlink_message_get_max_attribute(reply, &max_attr);
if (r < 0)
return r;
info.phys_port_id_supported = max_attr >= IFLA_PHYS_PORT_ID;
info.phys_switch_id_supported = max_attr >= IFLA_PHYS_SWITCH_ID;
info.phys_port_name_supported = max_attr >= IFLA_PHYS_PORT_NAME;
*ret = info;
info = LINK_INFO_NULL;
return 0;
}
static int cache_unsigned(sd_device *device, const char *attr, uint64_t val) {
_cleanup_free_ char *str = NULL;
int r;
assert(device);
assert(attr);
if (device_get_cached_sysattr_value(device, attr, NULL) != -ESTALE)
return 0;
if (asprintf(&str, "%"PRIu64, val) < 0)
return -ENOMEM;
r = device_cache_sysattr_value(device, attr, str);
if (r < 0)
return r;
TAKE_PTR(str);
return 0;
}
static int cache_hw_addr(sd_device *device, const char *attr, const struct hw_addr_data *hw_addr) {
_cleanup_free_ char *str = NULL;
int r;
assert(device);
assert(attr);
assert(hw_addr);
if (device_get_cached_sysattr_value(device, attr, NULL) != -ESTALE)
return 0;
str = new(char, HW_ADDR_TO_STRING_MAX);
if (!str)
return -ENOMEM;
r = device_cache_sysattr_value(device, attr, hw_addr_to_string(hw_addr, str));
if (r < 0)
return r;
TAKE_PTR(str);
return 0;
}
static int cache_binary(sd_device *device, const char *attr, size_t len, const uint8_t *data) {
_cleanup_free_ char *str = NULL;
int r;
assert(device);
assert(attr);
if (device_get_cached_sysattr_value(device, attr, NULL) != -ESTALE)
return 0;
if (data) {
size_t j = 0;
str = new(char, len * 2 + 1);
if (!str)
return -ENOMEM;
for (size_t i = 0; i < len; i++) {
str[j++] = hexchar(data[i] >> 4);
str[j++] = hexchar(data[i] & 0x0f);
}
str[j] = '\0';
}
r = device_cache_sysattr_value(device, attr, str);
if (r < 0)
return r;
TAKE_PTR(str);
return 0;
}
static int cache_string(sd_device *device, const char *attr, const char *val) {
_cleanup_free_ char *str = NULL;
int r;
assert(device);
assert(attr);
if (device_get_cached_sysattr_value(device, attr, NULL) != -ESTALE)
return 0;
if (val) {
str = strdup(val);
if (!str)
return -ENOMEM;
}
r = device_cache_sysattr_value(device, attr, str);
if (r < 0)
return r;
TAKE_PTR(str);
return 0;
}
int device_cache_sysattr_from_link_info(sd_device *device, LinkInfo *info) {
int ifindex, r;
assert(device);
assert(info);
r = sd_device_get_ifindex(device, &ifindex);
if (r < 0)
return r;
if (ifindex != info->ifindex)
return -EINVAL;
r = cache_unsigned(device, "type", info->iftype);
if (r < 0)
return r;
r = cache_unsigned(device, "addr_len", info->hw_addr.length);
if (r < 0)
return r;
r = cache_hw_addr(device, "address", &info->hw_addr);
if (r < 0)
return r;
r = cache_hw_addr(device, "broadcast", &info->broadcast);
if (r < 0)
return r;
r = cache_unsigned(device, "mtu", info->mtu);
if (r < 0)
return r;
r = cache_unsigned(device, "iflink", info->iflink);
if (r < 0)
return r;
r = cache_unsigned(device, "link_mode", info->link_mode);
if (r < 0)
return r;
r = cache_string(device, "ifalias", strempty(info->ifalias));
if (r < 0)
return r;
r = cache_unsigned(device, "netdev_group", info->group);
if (r < 0)
return r;
if (info->phys_port_id_supported) {
r = cache_binary(device, "phys_port_id", info->phys_port_id_len, info->phys_port_id);
if (r < 0)
return r;
}
if (info->phys_switch_id_supported) {
r = cache_binary(device, "phys_switch_id", info->phys_switch_id_len, info->phys_switch_id);
if (r < 0)
return r;
}
if (info->phys_port_name_supported) {
r = cache_string(device, "phys_port_name", info->phys_port_name);
if (r < 0)
return r;
}
return 0;
}
int device_get_sysattr_value_maybe_from_netlink(
sd_device *device,
sd_netlink **rtnl,
const char *sysattr,
const char **ret_value) {
_cleanup_(link_info_clear) LinkInfo info = LINK_INFO_NULL;
int ifindex, r;
assert(device);
assert(rtnl);
assert(sysattr);
if (sd_device_get_ifindex(device, &ifindex) < 0)
return sd_device_get_sysattr_value(device, sysattr, ret_value);
if (!STR_IN_SET(sysattr,
"type", "addr_len", "address", "broadcast", "mtu", "iflink", "linkmode",
"ifalias", "group", "phys_port_id", "phys_switch_id", "phys_port_name"))
return sd_device_get_sysattr_value(device, sysattr, ret_value);
r = device_get_cached_sysattr_value(device, sysattr, ret_value);
if (r != -ESTALE)
return r;
r = link_info_get(rtnl, ifindex, &info);
if (r < 0)
return r;
r = device_cache_sysattr_from_link_info(device, &info);
if (r < 0)
return r;
/* Do not use device_get_cached_sysattr_value() here, as kernel may not support
* IFLA_PHYS_PORT_NAME, and in that case we need to read the value from sysfs. */
return sd_device_get_sysattr_value(device, sysattr, ret_value);
}

41
src/udev/udev-netlink.h Normal file
View File

@ -0,0 +1,41 @@
/* SPDX-License-Identifier: LGPL-2.1-or-later */
#pragma once
#include "sd-device.h"
#include "sd-netlink.h"
#include "ether-addr-util.h"
typedef struct LinkInfo {
int ifindex;
uint16_t iftype; /* ARPHRD_* (type) */
struct hw_addr_data hw_addr; /* IFLA_ADDRESS (address, addr_len) */
struct hw_addr_data broadcast; /* IFLA_BROADCAST (broadcast) */
char *ifname; /* IFLA_IFNAME */
uint32_t mtu; /* IFLA_MTU (mtu) */
uint32_t iflink; /* IFLA_LINK (iflink) */
uint8_t link_mode; /* IFLA_LINKMODE (link_mode) */
char *ifalias; /* IFLA_IFALIAS (ifalias) */
uint32_t group; /* IFLA_GROUP (netdev_group) */
uint8_t *phys_port_id; /* IFLA_PHYS_PORT_ID (phys_port_id) */
size_t phys_port_id_len;
uint8_t *phys_switch_id; /* IFLA_PHYS_SWITCH_ID (phys_switch_id) */
size_t phys_switch_id_len;
char *phys_port_name; /* IFLA_PHYS_PORT_NAME (phys_port_name) */
bool phys_port_id_supported;
bool phys_switch_id_supported;
bool phys_port_name_supported;
} LinkInfo;
#define LINK_INFO_NULL ((LinkInfo) {})
void link_info_clear(LinkInfo *info);
int link_info_get(sd_netlink **rtnl, int ifindex, LinkInfo *ret);
int device_cache_sysattr_from_link_info(sd_device *device, LinkInfo *info);
int device_get_sysattr_value_maybe_from_netlink(
sd_device *device,
sd_netlink **rtnl,
const char *sysattr,
const char **ret_value);

View File

@ -29,6 +29,7 @@
#include "syslog-util.h"
#include "udev-builtin.h"
#include "udev-event.h"
#include "udev-netlink.h"
#include "udev-rules.h"
#include "udev-util.h"
#include "user-util.h"
@ -1396,7 +1397,7 @@ static bool token_match_attr(UdevRuleToken *token, sd_device *dev, UdevEvent *ev
name = nbuf;
_fallthrough_;
case SUBST_TYPE_PLAIN:
if (sd_device_get_sysattr_value(dev, name, &value) < 0)
if (device_get_sysattr_value_maybe_from_netlink(dev, &event->rtnl, name, &value) < 0)
return false;
break;
case SUBST_TYPE_SUBSYS: