[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