diff --git a/man/udev.xml b/man/udev.xml index 8782bb15c5..f6ea2abc12 100644 --- a/man/udev.xml +++ b/man/udev.xml @@ -117,7 +117,7 @@ := - Assign a value to a key finally; disallow any later changes. + Assign a value to a key finally; disallow any later changes. @@ -607,9 +607,12 @@ - Usually, control and other possibly unsafe characters are replaced - in strings used for device naming. The mode of replacement can be specified - with this option. + When replace, possibly unsafe characters in strings + assigned to NAME, SYMLINK, and + ENV{key} are replaced. When + none, no replacement is performed. When unset, the replacement + is performed for NAME, SYMLINK, but not for + ENV{key}. Defaults to unset. diff --git a/rules.d/60-persistent-storage.rules b/rules.d/60-persistent-storage.rules index 50b357f8df..b261821214 100644 --- a/rules.d/60-persistent-storage.rules +++ b/rules.d/60-persistent-storage.rules @@ -38,13 +38,13 @@ KERNEL=="nvme*[0-9]n*[0-9]", ENV{DEVTYPE}=="disk", ATTRS{wwid}=="?*", ENV{ID_WWN KERNEL=="nvme*[0-9]n*[0-9]", ENV{DEVTYPE}=="disk", ATTRS{model}=="?*", ENV{ID_MODEL}="$attr{model}" KERNEL=="nvme*[0-9]n*[0-9]", ENV{DEVTYPE}=="disk", ATTRS{firmware_rev}=="?*", ENV{ID_REVISION}="$attr{firmware_rev}" KERNEL=="nvme*[0-9]n*[0-9]", ENV{DEVTYPE}=="disk", ENV{ID_MODEL}=="?*", ENV{ID_SERIAL_SHORT}=="?*", \ - ENV{ID_SERIAL}="$env{ID_MODEL}_$env{ID_SERIAL_SHORT}", SYMLINK+="disk/by-id/nvme-$env{ID_SERIAL}" + OPTIONS="string_escape=replace", ENV{ID_SERIAL}="$env{ID_MODEL}_$env{ID_SERIAL_SHORT}", SYMLINK+="disk/by-id/nvme-$env{ID_SERIAL}" KERNEL=="nvme*[0-9]n*[0-9]p*[0-9]", ENV{DEVTYPE}=="partition", ATTRS{serial}=="?*", ENV{ID_SERIAL_SHORT}="$attr{serial}" KERNEL=="nvme*[0-9]n*[0-9]p*[0-9]", ENV{DEVTYPE}=="partition", ATTRS{model}=="?*", ENV{ID_MODEL}="$attr{model}" KERNEL=="nvme*[0-9]n*[0-9]p*[0-9]", ENV{DEVTYPE}=="partition", ATTRS{firmware_rev}=="?*", ENV{ID_REVISION}="$attr{firmware_rev}" KERNEL=="nvme*[0-9]n*[0-9]p*[0-9]", ENV{DEVTYPE}=="partition", ENV{ID_MODEL}=="?*", ENV{ID_SERIAL_SHORT}=="?*", \ - ENV{ID_SERIAL}="$env{ID_MODEL}_$env{ID_SERIAL_SHORT}", SYMLINK+="disk/by-id/nvme-$env{ID_SERIAL}-part%n" + OPTIONS="string_escape=replace", ENV{ID_SERIAL}="$env{ID_MODEL}_$env{ID_SERIAL_SHORT}", SYMLINK+="disk/by-id/nvme-$env{ID_SERIAL}-part%n" # virtio-blk KERNEL=="vd*[!0-9]", ATTRS{serial}=="?*", ENV{ID_SERIAL}="$attr{serial}", SYMLINK+="disk/by-id/virtio-$env{ID_SERIAL}" diff --git a/src/basic/socket-util.c b/src/basic/socket-util.c index f6e751a09f..1c353e86b0 100644 --- a/src/basic/socket-util.c +++ b/src/basic/socket-util.c @@ -748,6 +748,22 @@ static const char* const ip_tos_table[] = { DEFINE_STRING_TABLE_LOOKUP_WITH_FALLBACK(ip_tos, int, 0xff); +bool ifname_valid_char(char a) { + if ((unsigned char) a >= 127U) + return false; + + if ((unsigned char) a <= 32U) + return false; + + if (IN_SET(a, + ':', /* colons are used by the legacy "alias" interface logic */ + '/', /* slashes cannot work, since we need to use network interfaces in sysfs paths, and in paths slashes are separators */ + '%')) /* %d is used in the kernel's weird foo%d format string naming feature which we really really don't want to ever run into by accident */ + return false; + + return true; +} + bool ifname_valid_full(const char *p, IfnameValidFlags flags) { bool numeric = true; @@ -781,16 +797,7 @@ bool ifname_valid_full(const char *p, IfnameValidFlags flags) { return false; for (const char *t = p; *t; t++) { - if ((unsigned char) *t >= 127U) - return false; - - if ((unsigned char) *t <= 32U) - return false; - - if (IN_SET(*t, - ':', /* colons are used by the legacy "alias" interface logic */ - '/', /* slashes cannot work, since we need to use network interfaces in sysfs paths, and in paths slashes are separators */ - '%')) /* %d is used in the kernel's weird foo%d format string naming feature which we really really don't want to ever run into by accident */ + if (!ifname_valid_char(*t)) return false; numeric = numeric && (*t >= '0' && *t <= '9'); diff --git a/src/basic/socket-util.h b/src/basic/socket-util.h index e0b959f5da..f92e425fd6 100644 --- a/src/basic/socket-util.h +++ b/src/basic/socket-util.h @@ -139,6 +139,7 @@ typedef enum { IFNAME_VALID_NUMERIC = 1 << 1, _IFNAME_VALID_ALL = IFNAME_VALID_ALTERNATIVE | IFNAME_VALID_NUMERIC, } IfnameValidFlags; +bool ifname_valid_char(char a); bool ifname_valid_full(const char *p, IfnameValidFlags flags); static inline bool ifname_valid(const char *p) { return ifname_valid_full(p, 0); diff --git a/src/shared/netif-naming-scheme.h b/src/shared/netif-naming-scheme.h index c0f94d0407..119b80178f 100644 --- a/src/shared/netif-naming-scheme.h +++ b/src/shared/netif-naming-scheme.h @@ -34,6 +34,7 @@ typedef enum NamingSchemeFlags { NAMING_BRIDGE_NO_SLOT = 1 << 9, /* Don't use PCI hotplug slot information if the corresponding device is a PCI bridge */ NAMING_SLOT_FUNCTION_ID = 1 << 10, /* Use function_id if present to identify PCI hotplug slots */ NAMING_16BIT_INDEX = 1 << 11, /* Allow full 16-bit for the onboard index */ + NAMING_REPLACE_STRICTLY = 1 << 12, /* Use udev_replace_ifname() for NAME= rule */ /* And now the masks that combine the features above */ NAMING_V238 = 0, @@ -43,7 +44,7 @@ typedef enum NamingSchemeFlags { NAMING_V243 = NAMING_V241 | NAMING_NETDEVSIM | NAMING_LABEL_NOPREFIX, NAMING_V245 = NAMING_V243 | NAMING_NSPAWN_LONG_HASH, NAMING_V247 = NAMING_V245 | NAMING_BRIDGE_NO_SLOT, - NAMING_V249 = NAMING_V247 | NAMING_SLOT_FUNCTION_ID | NAMING_16BIT_INDEX, + NAMING_V249 = NAMING_V247 | NAMING_SLOT_FUNCTION_ID | NAMING_16BIT_INDEX | NAMING_REPLACE_STRICTLY, _NAMING_SCHEME_FLAGS_INVALID = -EINVAL, } NamingSchemeFlags; diff --git a/src/shared/udev-util.c b/src/shared/udev-util.c index 06aede9d36..f934fc157e 100644 --- a/src/shared/udev-util.c +++ b/src/shared/udev-util.c @@ -18,6 +18,7 @@ #include "parse-util.h" #include "path-util.h" #include "signal-util.h" +#include "socket-util.h" #include "string-table.h" #include "string-util.h" #include "strxcpyx.h" @@ -436,6 +437,22 @@ size_t udev_replace_whitespace(const char *str, char *to, size_t len) { return j; } +size_t udev_replace_ifname(char *str) { + size_t replaced = 0; + + assert(str); + + /* See ifname_valid_full(). */ + + for (char *p = str; *p != '\0'; p++) + if (!ifname_valid_char(*p)) { + *p = '_'; + replaced++; + } + + return replaced; +} + size_t udev_replace_chars(char *str, const char *allow) { size_t i = 0, replaced = 0; diff --git a/src/shared/udev-util.h b/src/shared/udev-util.h index d1c33b86a7..276686da80 100644 --- a/src/shared/udev-util.h +++ b/src/shared/udev-util.h @@ -46,6 +46,7 @@ void log_device_uevent(sd_device *device, const char *str); int udev_rule_parse_value(char *str, char **ret_value, char **ret_endpos); size_t udev_replace_whitespace(const char *str, char *to, size_t len); +size_t udev_replace_ifname(char *str); size_t udev_replace_chars(char *str, const char *allow); int udev_resolve_subsys_kernel(const char *string, char *result, size_t maxsize, bool read_value); diff --git a/src/udev/udev-event.c b/src/udev/udev-event.c index 8a01e2512e..b28089be71 100644 --- a/src/udev/udev-event.c +++ b/src/udev/udev-event.c @@ -17,6 +17,7 @@ #include "fd-util.h" #include "fs-util.h" #include "format-util.h" +#include "netif-naming-scheme.h" #include "netlink-util.h" #include "parse-util.h" #include "path-util.h" @@ -848,6 +849,12 @@ static int rename_netif(UdevEvent *event) { if (r < 0) return log_device_error_errno(dev, r, "Failed to get ifindex: %m"); + if (naming_scheme_has(NAMING_REPLACE_STRICTLY) && + !ifname_valid(event->name)) { + log_device_warning(dev, "Invalid network interface name, ignoring: %s", event->name); + return 0; + } + /* Set ID_RENAMING boolean property here, and drop it in the corresponding move uevent later. */ r = device_add_property(dev, "ID_RENAMING", "1"); if (r < 0) diff --git a/src/udev/udev-rules.c b/src/udev/udev-rules.c index c37c86a541..8713186226 100644 --- a/src/udev/udev-rules.c +++ b/src/udev/udev-rules.c @@ -17,6 +17,7 @@ #include "glob-util.h" #include "list.h" #include "mkdir.h" +#include "netif-naming-scheme.h" #include "nulstr-util.h" #include "parse-util.h" #include "path-util.h" @@ -2023,6 +2024,12 @@ static int udev_rule_apply_token_to_event( l = strpcpyl(&p, l, val, " ", NULL); (void) udev_event_apply_format(event, token->value, p, l, false); + if (event->esc == ESCAPE_REPLACE) { + count = udev_replace_chars(buf, NULL); + if (count > 0) + log_rule_debug(dev, rules, "Replaced %zu slash(es) from result of ENV{%s}%s=\"%s\"", + count, name, token->op == OP_ADD ? "+" : "", token->value); + } r = device_add_property(dev, name, value_new); if (r < 0) @@ -2053,21 +2060,23 @@ static int udev_rule_apply_token_to_event( if (token->op == OP_ASSIGN_FINAL) event->name_final = true; + if (sd_device_get_ifindex(dev, NULL) < 0) { + log_rule_error(dev, rules, + "Only network interface can be renamed, ignoring NAME=\"%s\"; please fix it.", + token->value); + break; + } + (void) udev_event_apply_format(event, token->value, buf, sizeof(buf), false); if (IN_SET(event->esc, ESCAPE_UNSET, ESCAPE_REPLACE)) { - count = udev_replace_chars(buf, "/"); + if (naming_scheme_has(NAMING_REPLACE_STRICTLY)) + count = udev_replace_ifname(buf); + else + count = udev_replace_chars(buf, "/"); if (count > 0) log_rule_debug(dev, rules, "Replaced %zu character(s) from result of NAME=\"%s\"", count, token->value); } - if (sd_device_get_devnum(dev, NULL) >= 0 && - (sd_device_get_devname(dev, &val) < 0 || - !streq_ptr(buf, path_startswith(val, "/dev/")))) { - log_rule_error(dev, rules, - "Kernel device nodes cannot be renamed, ignoring NAME=\"%s\"; please fix it.", - token->value); - break; - } r = free_and_strdup_warn(&event->name, buf); if (r < 0) return r; @@ -2096,7 +2105,8 @@ static int udev_rule_apply_token_to_event( else count = 0; if (count > 0) - log_rule_debug(dev, rules, "Replaced %zu character(s) from result of LINK", count); + log_rule_debug(dev, rules, "Replaced %zu character(s) from result of SYMLINK=\"%s\"", + count, token->value); p = skip_leading_chars(buf, NULL); while (!isempty(p)) {