[libc-commits] [libc] [libc] Add if_nameindex and if_freenameindex as an experimental entrypoint (PR #208438)

Pavel Labath via libc-commits libc-commits at lists.llvm.org
Wed Jul 15 05:51:02 PDT 2026


================
@@ -0,0 +1,541 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Unittests for if_nameindex and if_freenameindex.
+///
+//===----------------------------------------------------------------------===//
+
+#include "hdr/errno_macros.h"
+#include "hdr/net_if_macros.h"
+#include "hdr/types/socklen_t.h"
+#include "hdr/types/ssize_t.h"
+#include "hdr/types/struct_if_nameindex.h"
+#include "src/__support/CPP/span.h"
+#include "src/__support/CPP/string.h"
+#include "src/__support/CPP/string_view.h"
+#include "src/__support/CPP/tuple.h"
+#include "src/__support/CPP/type_traits/type_identity.h"
+#include "src/__support/error_or.h"
+#include "src/__support/fixedvector.h"
+#include "src/net/if_freenameindex.h"
+#include "src/net/if_nameindex.h"
+#include "src/net/linux/if_nameindex.h"
+#include "src/string/memory_utils/inline_memcpy.h"
+#include "src/string/memory_utils/inline_memset.h"
+#include "test/UnitTest/ErrnoCheckingTest.h"
+#include "test/UnitTest/Test.h"
+
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+
+using LIBC_NAMESPACE::Error;
+using LIBC_NAMESPACE::ErrorOr;
+using LIBC_NAMESPACE::FixedVector;
+using LIBC_NAMESPACE::cpp::get;
+using LIBC_NAMESPACE::cpp::span;
+using LIBC_NAMESPACE::cpp::string;
+using LIBC_NAMESPACE::cpp::string_view;
+using LIBC_NAMESPACE::cpp::tuple;
+
+template <typename T, size_t CAPACITY>
+static T
+pop_front_or(FixedVector<T, CAPACITY> &vec,
+             typename LIBC_NAMESPACE::cpp::type_identity<T>::type default_val) {
+  if (vec.empty())
+    return default_val;
+  // TODO: Add front() and erase() to FixedVector, then clean this up.
+  T first = vec[0];
+  for (size_t i = 1; i < vec.size(); ++i)
+    vec[i - 1] = vec[i];
+  vec.pop_back();
+  return first;
+}
+
+// TODO: Add string::operator+=(string_view), then remove this helper.
+static void append_bytes(string &str, const void *data, size_t len) {
+  size_t old_size = str.size();
+  str.resize(old_size + len);
+  LIBC_NAMESPACE::inline_memcpy(str.data() + old_size, data, len);
+}
+
+namespace {
+
+struct FakeNetworkSyscallPolicyData {
+  FixedVector<tuple<int, int, int>, 10> socket_calls;
+  FixedVector<ErrorOr<int>, 10> socket_results;
+  FixedVector<tuple<int, size_t, int>, 10> sendto_calls;
+  FixedVector<ErrorOr<ssize_t>, 10> sendto_results;
+  // TODO: Move this into sendto_calls when FixedVector and tuple support
+  // non-trivial types.
+  string sendto_data;
+  FixedVector<int, 10> close_calls;
+  FixedVector<ErrorOr<int>, 10> close_results;
+  FixedVector<tuple<int, size_t, int>, 10> recv_calls;
+  // TODO: Make this ErrorOr<string> when ErrorOr and FixedVector support
+  // non-trivial types.
+  FixedVector<ErrorOr<span<const uint8_t>>, 10> recv_results;
+};
+
+template <FakeNetworkSyscallPolicyData *DATA> struct FakeNetworkSyscallPolicy {
+  static ErrorOr<int> socket(int domain, int type, int protocol) {
+    DATA->socket_calls.push_back(tuple<int, int, int>(domain, type, protocol));
+    return pop_front_or(DATA->socket_results, Error(ENFILE));
+  }
+
+  static ErrorOr<ssize_t> sendto(int fd, const void *buf, size_t len, int flags,
+                                 const struct sockaddr *, socklen_t) {
+    DATA->sendto_calls.push_back(tuple<int, size_t, int>(fd, len, flags));
+    append_bytes(DATA->sendto_data, buf, len);
+    return pop_front_or(DATA->sendto_results, static_cast<ssize_t>(len));
+  }
+
+  static ErrorOr<ssize_t> recvfrom(int fd, void *buf, size_t len, int flags,
+                                   struct sockaddr *, socklen_t *) {
+    DATA->recv_calls.push_back(tuple<int, size_t, int>(fd, len, flags));
+    ErrorOr<span<const uint8_t>> chunk =
+        pop_front_or(DATA->recv_results, span<const uint8_t>());
+    if (!chunk.has_value())
+      return Error(chunk.error());
+    if (chunk->size() > len)
+      return Error(EINVAL);
+    LIBC_NAMESPACE::inline_memcpy(buf, chunk->data(), chunk->size());
+    return static_cast<ssize_t>(chunk->size());
+  }
+
+  static ErrorOr<int> close(int fd) {
+    DATA->close_calls.push_back(fd);
+    return pop_front_or(DATA->close_results, 0);
+  }
+};
+
+struct LlvmLibcIfNameIndexTest
+    : public LIBC_NAMESPACE::testing::ErrnoCheckingTest {
+  static constexpr int FAKE_SOCKET = 47;
+  static FakeNetworkSyscallPolicyData policy_data;
+  using Policy = FakeNetworkSyscallPolicy<&policy_data>;
+
+  void SetUp() override {
+    ErrnoCheckingTest::SetUp();
+    policy_data = {};
+  }
+  void validate_dump_request() {
+    ASSERT_EQ(policy_data.sendto_calls.size(), size_t(1));
+    ASSERT_EQ(get<0>(policy_data.sendto_calls[0]), FAKE_SOCKET);
+    ASSERT_EQ(get<2>(policy_data.sendto_calls[0]), 0);
+    ASSERT_EQ(policy_data.sendto_data.size(),
+              static_cast<size_t>(NLMSG_LENGTH(sizeof(ifinfomsg))));
+    auto *nlh =
+        reinterpret_cast<struct nlmsghdr *>(policy_data.sendto_data.data());
+    ASSERT_EQ(nlh->nlmsg_len,
+              static_cast<uint32_t>(NLMSG_LENGTH(sizeof(ifinfomsg))));
+    ASSERT_EQ(nlh->nlmsg_type, static_cast<uint16_t>(RTM_GETLINK));
+    ASSERT_EQ(nlh->nlmsg_flags,
+              static_cast<uint16_t>(NLM_F_REQUEST | NLM_F_DUMP));
+    auto *ifm = reinterpret_cast<struct ifinfomsg *>(NLMSG_DATA(nlh));
+    ASSERT_EQ(ifm->ifi_family, static_cast<unsigned char>(AF_UNSPEC));
+  }
+};
+
+struct LlvmLibcIfNameIndexSocketTest : public LlvmLibcIfNameIndexTest {
+  void SetUp() override {
+    LlvmLibcIfNameIndexTest::SetUp();
+    policy_data.socket_results.push_back(FAKE_SOCKET);
+  }
+
+  void TearDown() override {
+    ASSERT_EQ(policy_data.socket_calls.size(), size_t(1));
+    ASSERT_EQ(get<0>(policy_data.socket_calls[0]), AF_NETLINK);
+    ASSERT_EQ(get<1>(policy_data.socket_calls[0]), SOCK_RAW | SOCK_CLOEXEC);
+    ASSERT_EQ(get<2>(policy_data.socket_calls[0]), NETLINK_ROUTE);
+
+    ASSERT_EQ(policy_data.close_calls.size(), size_t(1));
+    ASSERT_EQ(policy_data.close_calls[0], FAKE_SOCKET);
+    ASSERT_TRUE(policy_data.sendto_results.empty());
+    ASSERT_TRUE(policy_data.recv_results.empty());
+    LlvmLibcIfNameIndexTest::TearDown();
+  }
+};
+
+} // namespace
+
+FakeNetworkSyscallPolicyData LlvmLibcIfNameIndexTest::policy_data;
+
+struct AttrName {
+  string_view name;
+  bool null_terminate = true;
+  size_t payload_len() const { return name.size() + (null_terminate ? 1 : 0); }
+  void write(uint8_t *dest) const {
+    LIBC_NAMESPACE::inline_memcpy(dest, name.data(), name.size());
+    if (null_terminate)
+      dest[name.size()] = '\0';
+  }
+  static constexpr uint16_t TYPE = IFLA_IFNAME;
+};
+
+template <uint16_t ATTR_TYPE> struct AttrInt {
+  unsigned int value;
+  size_t payload_len() const { return sizeof(unsigned int); }
+  void write(uint8_t *dest) const {
+    LIBC_NAMESPACE::inline_memcpy(dest, &value, sizeof(unsigned int));
+  }
+  static constexpr uint16_t TYPE = ATTR_TYPE;
+};
+
+using AttrMtu = AttrInt<IFLA_MTU>;
+using AttrTxqlen = AttrInt<IFLA_TXQLEN>;
----------------
labath wrote:

Added. The meaning of attributes is not important. We're just using this to test the parsing/skipping code.

https://github.com/llvm/llvm-project/pull/208438


More information about the libc-commits mailing list