]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
resolvconf-compat: first parse provided interface name as is
authorYu Watanabe <watanabe.yu+github@gmail.com>
Tue, 18 Oct 2022 08:18:55 +0000 (17:18 +0900)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Mon, 24 Oct 2022 11:34:10 +0000 (20:34 +0900)
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.

src/resolve/resolvectl.c
src/resolve/resolvectl.h

index 25ba1291187383a8bb7b69c26d7e1a16d5f423e0..b07761a4957d9477c94bc46ae197b29424610d8a 100644 (file)
@@ -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) {
index 1d0f14734836966603f0c0a1f3660e7ba7f545cf..3e404dad1026c2ae1ce6583fc51628b1a727abc5 100644 (file)
@@ -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);
+}