summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYu Watanabe <watanabe.yu+github@gmail.com>2022-10-18 17:18:55 +0900
committerYu Watanabe <watanabe.yu+github@gmail.com>2022-10-24 20:34:10 +0900
commit7d4e850323b4270979ac55dd0e6893a7c348c7e2 (patch)
treeaa9eec29e07fa0bc56a14e8b4ab8f55343687652
parent427eeb44f229c8613b0f3d7f06bf4eff6c55a00f (diff)
downloadsystemd-7d4e850323b4270979ac55dd0e6893a7c348c7e2.tar.gz
resolvconf-compat: first parse provided interface name as is
Then, try to drop multiple protocol specifiers at the end. Strictly speaking, this breaks backward compatibility: if eth0 and eth0.42 exists, then previously, echo 'nameserver 192.168.0.1' | resolvconf -a eth0.42 adds the DNS server to eth0 instead of eth0.42, as we unconditionally dropped the specifier after the last dot, and echo 'nameserver 192.168.0.1' | resolvconf -a eth0.42.dhcp adds the DNS server to eth0.42. However, with this commit, now the both commands add the DNS server to eth0.42. But, hopefully, this should be preferable behavior. Fixes #25032.
-rw-r--r--src/resolve/resolvectl.c87
-rw-r--r--src/resolve/resolvectl.h9
2 files changed, 62 insertions, 34 deletions
diff --git a/src/resolve/resolvectl.c b/src/resolve/resolvectl.c
index 25ba129118..b07761a495 100644
--- a/src/resolve/resolvectl.c
+++ b/src/resolve/resolvectl.c
@@ -108,51 +108,74 @@ static int interface_info_compare(const InterfaceInfo *a, const InterfaceInfo *b
return strcmp_ptr(a->name, b->name);
}
-int ifname_mangle(const char *s) {
- _cleanup_free_ char *iface = NULL;
- int ifi;
+int ifname_mangle_full(const char *s, bool drop_protocol_specifier) {
+ _cleanup_(sd_netlink_unrefp) sd_netlink *rtnl = NULL;
+ _cleanup_strv_free_ char **found = NULL;
+ int r;
assert(s);
- iface = strdup(s);
- if (!iface)
- return log_oom();
+ if (drop_protocol_specifier) {
+ _cleanup_free_ char *buf = NULL;
+ int ifindex_longest_name = -ENODEV;
- ifi = rtnl_resolve_interface(NULL, iface);
- if (ifi < 0) {
- if (ifi == -ENODEV && arg_ifindex_permissive) {
- log_debug("Interface '%s' not found, but -f specified, ignoring.", iface);
- return 0; /* done */
- }
+ /* When invoked as resolvconf, drop the protocol specifier(s) at the end. */
- return log_error_errno(ifi, "Failed to resolve interface \"%s\": %m", iface);
- }
+ buf = strdup(s);
+ if (!buf)
+ return log_oom();
- if (arg_ifindex > 0 && arg_ifindex != ifi)
- return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Specified multiple different interfaces. Refusing.");
+ for (;;) {
+ r = rtnl_resolve_interface(&rtnl, buf);
+ if (r > 0) {
+ if (ifindex_longest_name <= 0)
+ ifindex_longest_name = r;
- arg_ifindex = ifi;
- free_and_replace(arg_ifname, iface);
+ r = strv_extend(&found, buf);
+ if (r < 0)
+ return log_oom();
+ }
- return 1;
-}
+ char *dot = strrchr(buf, '.');
+ if (!dot)
+ break;
-int ifname_resolvconf_mangle(const char *s) {
- const char *dot;
+ *dot = '\0';
+ }
- assert(s);
+ unsigned n = strv_length(found);
+ if (n > 1) {
+ _cleanup_free_ char *joined = NULL;
- dot = strrchr(s, '.');
- if (dot) {
- _cleanup_free_ char *iface = NULL;
+ joined = strv_join(found, ", ");
+ log_warning("Found multiple interfaces (%s) matching with '%s'. Using '%s' (ifindex=%i).",
+ strna(joined), s, found[0], ifindex_longest_name);
- log_debug("Ignoring protocol specifier '%s'.", dot + 1);
- iface = strndup(s, dot - s);
- if (!iface)
- return log_oom();
- return ifname_mangle(iface);
+ } else if (n == 1) {
+ const char *proto;
+
+ proto = ASSERT_PTR(startswith(s, found[0]));
+ if (!isempty(proto))
+ log_info("Dropped protocol specifier '%s' from '%s'. Using '%s' (ifindex=%i).",
+ proto, s, found[0], ifindex_longest_name);
+ }
+
+ r = ifindex_longest_name;
} else
- return ifname_mangle(s);
+ r = rtnl_resolve_interface(&rtnl, s);
+ if (r < 0) {
+ if (ERRNO_IS_DEVICE_ABSENT(r) && arg_ifindex_permissive) {
+ log_debug_errno(r, "Interface '%s' not found, but -f specified, ignoring: %m", s);
+ return 0; /* done */
+ }
+ return log_error_errno(r, "Failed to resolve interface \"%s\": %m", s);
+ }
+
+ if (arg_ifindex > 0 && arg_ifindex != r)
+ return log_error_errno(SYNTHETIC_ERRNO(EINVAL), "Specified multiple different interfaces. Refusing.");
+
+ arg_ifindex = r;
+ return free_and_strdup_warn(&arg_ifname, found ? found[0] : s); /* found */
}
static void print_source(uint64_t flags, usec_t rtt) {
diff --git a/src/resolve/resolvectl.h b/src/resolve/resolvectl.h
index 1d0f147348..3e404dad10 100644
--- a/src/resolve/resolvectl.h
+++ b/src/resolve/resolvectl.h
@@ -26,5 +26,10 @@ extern char **arg_set_dns;
extern char **arg_set_domain;
extern bool arg_ifindex_permissive;
-int ifname_mangle(const char *s);
-int ifname_resolvconf_mangle(const char *s);
+int ifname_mangle_full(const char *s, bool drop_protocol_specifier);
+static inline int ifname_mangle(const char *s) {
+ return ifname_mangle_full(s, false);
+}
+static inline int ifname_resolvconf_mangle(const char *s) {
+ return ifname_mangle_full(s, true);
+}