]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
sd-netlink: unify network interface name getter and resolvers
authorYu Watanabe <watanabe.yu+github@gmail.com>
Sat, 27 Jan 2024 17:49:22 +0000 (02:49 +0900)
committerYu Watanabe <watanabe.yu+github@gmail.com>
Tue, 30 Jan 2024 11:37:04 +0000 (20:37 +0900)
This makes rtnl_resolve_interface() always check the existence of the
resolved interface, which previously did not when a decimal formatted
ifindex is provided, e.g. "1" or "42".

src/libsystemd/sd-netlink/netlink-util.c
src/libsystemd/sd-netlink/netlink-util.h
src/test/test-socket-netlink.c

index 3713fadfb4b3274072183ea220676cfc2c8d74b4..e634fd33788f191706ae5021ce81320507b27b7c 100644 (file)
 #include "process-util.h"
 #include "strv.h"
 
+static int parse_newlink_message(
+                  sd_netlink_message *message,
+                  char **ret_name,
+                  char ***ret_altnames) {
+
+        _cleanup_strv_free_ char **altnames = NULL;
+        int r, ifindex;
+
+        assert(message);
+
+        uint16_t type;
+        r = sd_netlink_message_get_type(message, &type);
+        if (r < 0)
+                return r;
+        if (type != RTM_NEWLINK)
+                return -EPROTO;
+
+        r = sd_rtnl_message_link_get_ifindex(message, &ifindex);
+        if (r < 0)
+                return r;
+        if (ifindex <= 0)
+                return -EPROTO;
+
+        if (ret_altnames) {
+                r = sd_netlink_message_read_strv(message, IFLA_PROP_LIST, IFLA_ALT_IFNAME, &altnames);
+                if (r < 0 && r != -ENODATA)
+                        return r;
+        }
+
+        if (ret_name) {
+                r = sd_netlink_message_read_string_strdup(message, IFLA_IFNAME, ret_name);
+                if (r < 0)
+                        return r;
+        }
+
+        if (ret_altnames)
+                *ret_altnames = TAKE_PTR(altnames);
+
+        return ifindex;
+}
+
+int rtnl_get_ifname_full(sd_netlink **rtnl, int ifindex, char **ret_name, char ***ret_altnames) {
+        _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *message = NULL, *reply = NULL;
+        _cleanup_(sd_netlink_unrefp) sd_netlink *our_rtnl = NULL;
+        int r;
+
+        assert(ifindex > 0);
+
+        /* This is similar to if_indextoname(), but also optionally provides alternative names. */
+
+        if (!rtnl)
+                rtnl = &our_rtnl;
+        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 < 0)
+                return r;
+
+        return parse_newlink_message(reply, ret_name, ret_altnames);
+}
+
+int rtnl_resolve_ifname_full(
+                  sd_netlink **rtnl,
+                  ResolveInterfaceNameFlag flags,
+                  const char *name,
+                  char **ret_name,
+                  char ***ret_altnames) {
+
+        _cleanup_(sd_netlink_unrefp) sd_netlink *our_rtnl = NULL;
+        int r;
+
+        assert(name);
+        assert(flags > 0);
+
+        /* This is similar to if_nametoindex(), but also resolves alternative names and decimal formatted
+         * ifindex too. Returns ifindex, and optionally provides the main interface name and alternative
+         * names.*/
+
+        if (!rtnl)
+                rtnl = &our_rtnl;
+        if (!*rtnl) {
+                r = sd_netlink_open(rtnl);
+                if (r < 0)
+                        return r;
+        }
+
+        /* First, use IFLA_IFNAME */
+        if (FLAGS_SET(flags, RESOLVE_IFNAME_MAIN) && ifname_valid(name)) {
+                _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *message = NULL, *reply = NULL;
+
+                r = sd_rtnl_message_new_link(*rtnl, &message, RTM_GETLINK, 0);
+                if (r < 0)
+                        return r;
+
+                r = sd_netlink_message_append_string(message, IFLA_IFNAME, name);
+                if (r < 0)
+                        return r;
+
+                r = sd_netlink_call(*rtnl, message, 0, &reply);
+                if (r >= 0)
+                        return parse_newlink_message(reply, ret_name, ret_altnames);
+                if (r != -ENODEV)
+                        return r;
+        }
+
+        /* Next, try IFLA_ALT_IFNAME */
+        if (FLAGS_SET(flags, RESOLVE_IFNAME_ALTERNATIVE) &&
+            ifname_valid_full(name, IFNAME_VALID_ALTERNATIVE)) {
+                _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *message = NULL, *reply = NULL;
+
+                r = sd_rtnl_message_new_link(*rtnl, &message, RTM_GETLINK, 0);
+                if (r < 0)
+                        return r;
+
+                r = sd_netlink_message_append_string(message, IFLA_ALT_IFNAME, name);
+                if (r < 0)
+                        return r;
+
+                r = sd_netlink_call(*rtnl, message, 0, &reply);
+                if (r >= 0)
+                        return parse_newlink_message(reply, ret_name, ret_altnames);
+                /* The kernels older than 76c9ac0ee878f6693d398d3a95ccaf85e1f597a6 (v5.5) return -EINVAL. */
+                if (!IN_SET(r, -ENODEV, -EINVAL))
+                        return r;
+        }
+
+        /* Finally, assume the string is a decimal formatted ifindex. */
+        if (FLAGS_SET(flags, RESOLVE_IFNAME_NUMERIC)) {
+                int ifindex;
+
+                ifindex = parse_ifindex(name);
+                if (ifindex <= 0)
+                        return -ENODEV;
+
+                return rtnl_get_ifname_full(rtnl, ifindex, ret_name, ret_altnames);
+        }
+
+        return -ENODEV;
+}
+
 static int set_link_name(sd_netlink **rtnl, int ifindex, const char *name) {
         _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *message = NULL;
         int r;
@@ -242,38 +390,6 @@ int rtnl_set_link_properties(
         return 0;
 }
 
-int rtnl_get_link_alternative_names(sd_netlink **rtnl, int ifindex, char ***ret) {
-        _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *message = NULL, *reply = NULL;
-        _cleanup_strv_free_ char **names = NULL;
-        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 < 0)
-                return r;
-
-        r = sd_netlink_message_read_strv(reply, IFLA_PROP_LIST, IFLA_ALT_IFNAME, &names);
-        if (r < 0 && r != -ENODATA)
-                return r;
-
-        *ret = TAKE_PTR(names);
-
-        return 0;
-}
-
 static int rtnl_update_link_alternative_names(
                 sd_netlink **rtnl,
                 uint16_t nlmsg_type,
@@ -374,92 +490,6 @@ int rtnl_set_link_alternative_names_by_ifname(
         return 0;
 }
 
-int rtnl_resolve_link_alternative_name(sd_netlink **rtnl, const char *name, char **ret) {
-        _cleanup_(sd_netlink_unrefp) sd_netlink *our_rtnl = NULL;
-        _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *message = NULL, *reply = NULL;
-        int r, ifindex;
-
-        assert(name);
-
-        /* This returns ifindex and the main interface name. */
-
-        if (!ifname_valid_full(name, IFNAME_VALID_ALTERNATIVE))
-                return -EINVAL;
-
-        if (!rtnl)
-                rtnl = &our_rtnl;
-        if (!*rtnl) {
-                r = sd_netlink_open(rtnl);
-                if (r < 0)
-                        return r;
-        }
-
-        r = sd_rtnl_message_new_link(*rtnl, &message, RTM_GETLINK, 0);
-        if (r < 0)
-                return r;
-
-        r = sd_netlink_message_append_string(message, IFLA_ALT_IFNAME, name);
-        if (r < 0)
-                return r;
-
-        r = sd_netlink_call(*rtnl, message, 0, &reply);
-        if (r == -EINVAL)
-                return -ENODEV; /* The device doesn't exist */
-        if (r < 0)
-                return r;
-
-        r = sd_rtnl_message_link_get_ifindex(reply, &ifindex);
-        if (r < 0)
-                return r;
-        assert(ifindex > 0);
-
-        if (ret) {
-                r = sd_netlink_message_read_string_strdup(reply, IFLA_IFNAME, ret);
-                if (r < 0)
-                        return r;
-        }
-
-        return ifindex;
-}
-
-int rtnl_resolve_ifname(sd_netlink **rtnl, const char *name) {
-        int r;
-
-        /* Like if_nametoindex, but resolves "alternative names" too. */
-
-        assert(name);
-
-        r = if_nametoindex(name);
-        if (r > 0)
-                return r;
-
-        return rtnl_resolve_link_alternative_name(rtnl, name, NULL);
-}
-
-int rtnl_resolve_interface(sd_netlink **rtnl, const char *name) {
-        int r;
-
-        /* Like rtnl_resolve_ifname, but resolves interface numbers too. */
-
-        assert(name);
-
-        r = parse_ifindex(name);
-        if (r > 0)
-                return r;
-        assert(r < 0);
-
-        return rtnl_resolve_ifname(rtnl, name);
-}
-
-int rtnl_resolve_interface_or_warn(sd_netlink **rtnl, const char *name) {
-        int r;
-
-        r = rtnl_resolve_interface(rtnl, name);
-        if (r < 0)
-                return log_error_errno(r, "Failed to resolve interface \"%s\": %m", name);
-        return r;
-}
-
 int rtnl_get_link_info(
                 sd_netlink **rtnl,
                 int ifindex,
index 277b4a30cf7ac887fdfed7778e44a4d3c1a54b29..4ba64f0f93390efac369f1da9592e6c913c910e3 100644 (file)
@@ -19,6 +19,22 @@ typedef struct RouteVia {
         union in_addr_union address;
 } _packed_ RouteVia;
 
+int rtnl_get_ifname_full(sd_netlink **rtnl, int ifindex, char **ret_name, char ***ret_altnames);
+
+typedef enum ResolveInterfaceNameFlag {
+        RESOLVE_IFNAME_MAIN        = 1 << 0, /* resolve main interface name */
+        RESOLVE_IFNAME_ALTERNATIVE = 1 << 1, /* resolve alternative name */
+        RESOLVE_IFNAME_NUMERIC     = 1 << 2, /* resolve decimal formatted ifindex */
+        _RESOLVE_IFNAME_ALL        = RESOLVE_IFNAME_MAIN | RESOLVE_IFNAME_ALTERNATIVE | RESOLVE_IFNAME_NUMERIC,
+} ResolveInterfaceNameFlag;
+
+int rtnl_resolve_ifname_full(
+                  sd_netlink **rtnl,
+                  ResolveInterfaceNameFlag flags,
+                  const char *name,
+                  char **ret_name,
+                  char ***ret_altnames);
+
 int rtnl_rename_link(sd_netlink **rtnl, const char *orig_name, const char *new_name);
 int rtnl_set_link_name(sd_netlink **rtnl, int ifindex, const char *name, char* const* alternative_names);
 static inline int rtnl_append_link_alternative_names(sd_netlink **rtnl, int ifindex, char* const *alternative_names) {
@@ -35,14 +51,32 @@ int rtnl_set_link_properties(
                 uint32_t mtu,
                 uint32_t gso_max_size,
                 size_t gso_max_segments);
-int rtnl_get_link_alternative_names(sd_netlink **rtnl, int ifindex, char ***ret);
+static inline int rtnl_get_link_alternative_names(sd_netlink **rtnl, int ifindex, char ***ret) {
+        return rtnl_get_ifname_full(rtnl, ifindex, NULL, ret);
+}
 int rtnl_set_link_alternative_names(sd_netlink **rtnl, int ifindex, char* const *alternative_names);
 int rtnl_set_link_alternative_names_by_ifname(sd_netlink **rtnl, const char *ifname, char* const *alternative_names);
 int rtnl_delete_link_alternative_names(sd_netlink **rtnl, int ifindex, char* const *alternative_names);
-int rtnl_resolve_link_alternative_name(sd_netlink **rtnl, const char *name, char **ret);
-int rtnl_resolve_ifname(sd_netlink **rtnl, const char *name);
-int rtnl_resolve_interface(sd_netlink **rtnl, const char *name);
-int rtnl_resolve_interface_or_warn(sd_netlink **rtnl, const char *name);
+static inline int rtnl_resolve_link_alternative_name(sd_netlink **rtnl, const char *name, char **ret) {
+        return rtnl_resolve_ifname_full(rtnl, RESOLVE_IFNAME_ALTERNATIVE, name, ret, NULL);
+}
+static inline int rtnl_resolve_ifname(sd_netlink **rtnl, const char *name) {
+        return rtnl_resolve_ifname_full(rtnl, RESOLVE_IFNAME_MAIN | RESOLVE_IFNAME_ALTERNATIVE, name, NULL, NULL);
+}
+static inline int rtnl_resolve_interface(sd_netlink **rtnl, const char *name) {
+        return rtnl_resolve_ifname_full(rtnl, _RESOLVE_IFNAME_ALL, name, NULL, NULL);
+}
+static inline int rtnl_resolve_interface_or_warn(sd_netlink **rtnl, const char *name) {
+        int r;
+
+        assert(name);
+
+        r = rtnl_resolve_interface(rtnl, name);
+        if (r < 0)
+                return log_error_errno(r, "Failed to resolve interface \"%s\": %m", name);
+        return r;
+}
+
 int rtnl_get_link_info(
                 sd_netlink **rtnl,
                 int ifindex,
index ad642ca4b1facfe47b902bf95c15e8da500385c8..1cbfe6d66bd4a01f79d3a681dd17c57146598a01 100644 (file)
@@ -248,9 +248,9 @@ TEST(in_addr_ifindex_to_string) {
         test_in_addr_ifindex_to_string_one(AF_INET, "10.11.12.13", 9, "10.11.12.13");
         test_in_addr_ifindex_to_string_one(AF_INET6, "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff", 10, "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff");
         test_in_addr_ifindex_to_string_one(AF_INET6, "::1", 11, "::1");
-        test_in_addr_ifindex_to_string_one(AF_INET6, "fe80::", 12, "fe80::%12");
+        test_in_addr_ifindex_to_string_one(AF_INET6, "fe80::", LOOPBACK_IFINDEX, "fe80::%1");
         test_in_addr_ifindex_to_string_one(AF_INET6, "fe80::", 0, "fe80::");
-        test_in_addr_ifindex_to_string_one(AF_INET6, "fe80::14", 12, "fe80::14%12");
+        test_in_addr_ifindex_to_string_one(AF_INET6, "fe80::14", 0, "fe80::14");
         test_in_addr_ifindex_to_string_one(AF_INET6, "fe80::15", -7, "fe80::15");
         test_in_addr_ifindex_to_string_one(AF_INET6, "fe80::16", LOOPBACK_IFINDEX, "fe80::16%1");
 }
@@ -265,9 +265,9 @@ TEST(in_addr_ifindex_from_string_auto) {
         assert_se(family == AF_INET6);
         assert_se(ifindex == 0);
 
-        assert_se(in_addr_ifindex_from_string_auto("fe80::18%19", &family, &ua, &ifindex) >= 0);
+        assert_se(in_addr_ifindex_from_string_auto("fe80::18%1", &family, &ua, &ifindex) >= 0);
         assert_se(family == AF_INET6);
-        assert_se(ifindex == 19);
+        assert_se(ifindex == 1);
 
         assert_se(in_addr_ifindex_from_string_auto("fe80::18%lo", &family, &ua, &ifindex) >= 0);
         assert_se(family == AF_INET6);
@@ -288,8 +288,8 @@ static void test_in_addr_ifindex_name_from_string_auto_one(const char *a, const
 TEST(in_addr_ifindex_name_from_string_auto) {
         test_in_addr_ifindex_name_from_string_auto_one("192.168.0.1", NULL);
         test_in_addr_ifindex_name_from_string_auto_one("192.168.0.1#test.com", "test.com");
-        test_in_addr_ifindex_name_from_string_auto_one("fe80::18%19", NULL);
-        test_in_addr_ifindex_name_from_string_auto_one("fe80::18%19#another.test.com", "another.test.com");
+        test_in_addr_ifindex_name_from_string_auto_one("fe80::18%1", NULL);
+        test_in_addr_ifindex_name_from_string_auto_one("fe80::18%1#another.test.com", "another.test.com");
 }
 
 static void test_in_addr_port_ifindex_name_from_string_auto_one(const char *str, int family, uint16_t port, int ifindex,
@@ -356,15 +356,15 @@ TEST(in_addr_port_ifindex_name_from_string_auto) {
         test_in_addr_port_ifindex_name_from_string_auto_one("192.168.0.1:53#example.com", AF_INET, 53, 0, "example.com", NULL);
         test_in_addr_port_ifindex_name_from_string_auto_one("fe80::18", AF_INET6, 0, 0, NULL, NULL);
         test_in_addr_port_ifindex_name_from_string_auto_one("fe80::18#hoge.com", AF_INET6, 0, 0, "hoge.com", NULL);
-        test_in_addr_port_ifindex_name_from_string_auto_one("fe80::18%19", AF_INET6, 0, 19, NULL, NULL);
+        test_in_addr_port_ifindex_name_from_string_auto_one("fe80::18%1", AF_INET6, 0, 1, NULL, NULL);
         test_in_addr_port_ifindex_name_from_string_auto_one("fe80::18%lo", AF_INET6, 0, 1, NULL, "fe80::18%1");
         test_in_addr_port_ifindex_name_from_string_auto_one("[fe80::18]:53", AF_INET6, 53, 0, NULL, NULL);
-        test_in_addr_port_ifindex_name_from_string_auto_one("[fe80::18]:53%19", AF_INET6, 53, 19, NULL, NULL);
+        test_in_addr_port_ifindex_name_from_string_auto_one("[fe80::18]:53%1", AF_INET6, 53, 1, NULL, NULL);
         test_in_addr_port_ifindex_name_from_string_auto_one("[fe80::18]:53%lo", AF_INET6, 53, 1, NULL, "[fe80::18]:53%1");
-        test_in_addr_port_ifindex_name_from_string_auto_one("fe80::18%19#hoge.com", AF_INET6, 0, 19, "hoge.com", NULL);
+        test_in_addr_port_ifindex_name_from_string_auto_one("fe80::18%1#hoge.com", AF_INET6, 0, 1, "hoge.com", NULL);
         test_in_addr_port_ifindex_name_from_string_auto_one("[fe80::18]:53#hoge.com", AF_INET6, 53, 0, "hoge.com", NULL);
-        test_in_addr_port_ifindex_name_from_string_auto_one("[fe80::18]:53%19", AF_INET6, 53, 19, NULL, NULL);
-        test_in_addr_port_ifindex_name_from_string_auto_one("[fe80::18]:53%19#hoge.com", AF_INET6, 53, 19, "hoge.com", NULL);
+        test_in_addr_port_ifindex_name_from_string_auto_one("[fe80::18]:53%1", AF_INET6, 53, 1, NULL, NULL);
+        test_in_addr_port_ifindex_name_from_string_auto_one("[fe80::18]:53%1#hoge.com", AF_INET6, 53, 1, "hoge.com", NULL);
         test_in_addr_port_ifindex_name_from_string_auto_one("[fe80::18]:53%lo", AF_INET6, 53, 1, NULL, "[fe80::18]:53%1");
         test_in_addr_port_ifindex_name_from_string_auto_one("[fe80::18]:53%lo#hoge.com", AF_INET6, 53, 1, "hoge.com", "[fe80::18]:53%1#hoge.com");
 }