gvisor.dev/gvisor@v0.0.0-20240520182842-f9d4d51c7e0f/test/syscalls/linux/socket_netlink_route.cc (about)

     1  // Copyright 2018 The gVisor Authors.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  #include <arpa/inet.h>
    16  #include <fcntl.h>
    17  #include <ifaddrs.h>
    18  #include <linux/fib_rules.h>
    19  #include <linux/if.h>
    20  #include <linux/netlink.h>
    21  #include <linux/rtnetlink.h>
    22  #include <sys/socket.h>
    23  #include <sys/types.h>
    24  #include <unistd.h>
    25  
    26  #include <cerrno>
    27  #include <cstdint>
    28  #include <iostream>
    29  #include <utility>
    30  #include <vector>
    31  
    32  #include "gmock/gmock.h"
    33  #include "gtest/gtest.h"
    34  #include "absl/strings/str_format.h"
    35  #include "test/syscalls/linux/socket_netlink_route_util.h"
    36  #include "test/syscalls/linux/socket_netlink_util.h"
    37  #include "test/util/cleanup.h"
    38  #include "test/util/file_descriptor.h"
    39  #include "test/util/linux_capability_util.h"
    40  #include "test/util/posix_error.h"
    41  #include "test/util/save_util.h"
    42  #include "test/util/socket_util.h"
    43  #include "test/util/test_util.h"
    44  
    45  // Tests for NETLINK_ROUTE sockets.
    46  
    47  namespace gvisor {
    48  namespace testing {
    49  
    50  namespace {
    51  
    52  constexpr uint32_t kSeq = 12345;
    53  
    54  using ::testing::_;
    55  using ::testing::AnyOf;
    56  using ::testing::Eq;
    57  
    58  // Parameters for SockOptTest. They are:
    59  // 0: Socket option to query.
    60  // 1: A predicate to run on the returned sockopt value. Should return true if
    61  //    the value is considered ok.
    62  // 2: A description of what the sockopt value is expected to be. Should complete
    63  //    the sentence "<value> was unexpected, expected <description>"
    64  using SockOptTest = ::testing::TestWithParam<
    65      std::tuple<int, std::function<bool(int)>, std::string>>;
    66  
    67  TEST_P(SockOptTest, GetSockOpt) {
    68    int sockopt = std::get<0>(GetParam());
    69    auto verifier = std::get<1>(GetParam());
    70    std::string verifier_description = std::get<2>(GetParam());
    71  
    72    FileDescriptor fd =
    73        ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE));
    74  
    75    int res;
    76    socklen_t len = sizeof(res);
    77  
    78    EXPECT_THAT(getsockopt(fd.get(), SOL_SOCKET, sockopt, &res, &len),
    79                SyscallSucceeds());
    80  
    81    EXPECT_EQ(len, sizeof(res));
    82    EXPECT_TRUE(verifier(res)) << absl::StrFormat(
    83        "getsockopt(%d, SOL_SOCKET, %d, &res, &len) => res=%d was unexpected, "
    84        "expected %s",
    85        fd.get(), sockopt, res, verifier_description);
    86  }
    87  
    88  std::function<bool(int)> IsPositive() {
    89    return [](int val) { return val > 0; };
    90  }
    91  
    92  std::function<bool(int)> IsEqual(int target) {
    93    return [target](int val) { return val == target; };
    94  }
    95  
    96  INSTANTIATE_TEST_SUITE_P(
    97      NetlinkRouteTest, SockOptTest,
    98      ::testing::Values(
    99          std::make_tuple(SO_SNDBUF, IsPositive(), "positive send buffer size"),
   100          std::make_tuple(SO_RCVBUF, IsPositive(),
   101                          "positive receive buffer size"),
   102          std::make_tuple(SO_TYPE, IsEqual(SOCK_RAW),
   103                          absl::StrFormat("SOCK_RAW (%d)", SOCK_RAW)),
   104          std::make_tuple(SO_DOMAIN, IsEqual(AF_NETLINK),
   105                          absl::StrFormat("AF_NETLINK (%d)", AF_NETLINK)),
   106          std::make_tuple(SO_PROTOCOL, IsEqual(NETLINK_ROUTE),
   107                          absl::StrFormat("NETLINK_ROUTE (%d)", NETLINK_ROUTE)),
   108          std::make_tuple(SO_PASSCRED, IsEqual(0), "0")));
   109  
   110  // Validates the reponses to RTM_GETLINK + NLM_F_DUMP.
   111  void CheckGetLinkResponse(const struct nlmsghdr* hdr, int seq, int port) {
   112    EXPECT_THAT(hdr->nlmsg_type, AnyOf(Eq(RTM_NEWLINK), Eq(NLMSG_DONE)));
   113  
   114    EXPECT_TRUE((hdr->nlmsg_flags & NLM_F_MULTI) == NLM_F_MULTI)
   115        << std::hex << hdr->nlmsg_flags;
   116  
   117    EXPECT_EQ(hdr->nlmsg_seq, seq);
   118    EXPECT_EQ(hdr->nlmsg_pid, port);
   119  
   120    if (hdr->nlmsg_type != RTM_NEWLINK) {
   121      return;
   122    }
   123  
   124    // RTM_NEWLINK contains at least the header and ifinfomsg.
   125    EXPECT_GE(hdr->nlmsg_len, NLMSG_SPACE(sizeof(struct ifinfomsg)));
   126  
   127    // TODO(mpratt): Check ifinfomsg contents and following attrs.
   128  }
   129  
   130  TEST(NetlinkRouteTest, GetLinkDump) {
   131    FileDescriptor fd =
   132        ASSERT_NO_ERRNO_AND_VALUE(NetlinkBoundSocket(NETLINK_ROUTE));
   133    uint32_t port = ASSERT_NO_ERRNO_AND_VALUE(NetlinkPortID(fd.get()));
   134  
   135    // Loopback is common among all tests, check that it's found.
   136    bool loopbackFound = false;
   137    ASSERT_NO_ERRNO(DumpLinks(fd, kSeq, [&](const struct nlmsghdr* hdr) {
   138      CheckGetLinkResponse(hdr, kSeq, port);
   139      if (hdr->nlmsg_type != RTM_NEWLINK) {
   140        return;
   141      }
   142      ASSERT_GE(hdr->nlmsg_len, NLMSG_SPACE(sizeof(struct ifinfomsg)));
   143      const struct ifinfomsg* msg =
   144          reinterpret_cast<const struct ifinfomsg*>(NLMSG_DATA(hdr));
   145      std::cout << "Found interface idx=" << msg->ifi_index
   146                << ", type=" << std::hex << msg->ifi_type << std::endl;
   147      if (msg->ifi_type == ARPHRD_LOOPBACK) {
   148        loopbackFound = true;
   149        EXPECT_NE(msg->ifi_flags & IFF_LOOPBACK, 0);
   150      }
   151    }));
   152    EXPECT_TRUE(loopbackFound);
   153  }
   154  
   155  // CheckLinkMsg checks a netlink message against an expected link.
   156  void CheckLinkMsg(const struct nlmsghdr* hdr, const Link& link) {
   157    ASSERT_THAT(hdr->nlmsg_type, Eq(RTM_NEWLINK));
   158    ASSERT_GE(hdr->nlmsg_len, NLMSG_SPACE(sizeof(struct ifinfomsg)));
   159    const struct ifinfomsg* msg =
   160        reinterpret_cast<const struct ifinfomsg*>(NLMSG_DATA(hdr));
   161    EXPECT_EQ(msg->ifi_index, link.index);
   162  
   163    const struct rtattr* rta = FindRtAttr(hdr, msg, IFLA_IFNAME);
   164    EXPECT_NE(nullptr, rta) << "IFLA_IFNAME not found in message.";
   165    if (rta != nullptr) {
   166      std::string name(reinterpret_cast<const char*>(RTA_DATA(rta)));
   167      EXPECT_EQ(name, link.name);
   168    }
   169  }
   170  
   171  TEST(NetlinkRouteTest, GetLinkByIndex) {
   172    Link loopback_link = ASSERT_NO_ERRNO_AND_VALUE(LoopbackLink());
   173  
   174    FileDescriptor fd =
   175        ASSERT_NO_ERRNO_AND_VALUE(NetlinkBoundSocket(NETLINK_ROUTE));
   176  
   177    struct request {
   178      struct nlmsghdr hdr;
   179      struct ifinfomsg ifm;
   180    };
   181  
   182    struct request req = {};
   183    req.hdr.nlmsg_len = sizeof(req);
   184    req.hdr.nlmsg_type = RTM_GETLINK;
   185    req.hdr.nlmsg_flags = NLM_F_REQUEST;
   186    req.hdr.nlmsg_seq = kSeq;
   187    req.ifm.ifi_family = AF_UNSPEC;
   188    req.ifm.ifi_index = loopback_link.index;
   189  
   190    bool found = false;
   191    ASSERT_NO_ERRNO(NetlinkRequestResponse(
   192        fd, &req, sizeof(req),
   193        [&](const struct nlmsghdr* hdr) {
   194          CheckLinkMsg(hdr, loopback_link);
   195          found = true;
   196        },
   197        false));
   198    EXPECT_TRUE(found) << "Netlink response does not contain any links.";
   199  }
   200  
   201  TEST(NetlinkRouteTest, LinkUp) {
   202    SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_ADMIN)));
   203    SKIP_IF(IsRunningWithHostinet());
   204  
   205    Link loopback_link = ASSERT_NO_ERRNO_AND_VALUE(LoopbackLink());
   206  
   207    FileDescriptor fd =
   208        ASSERT_NO_ERRNO_AND_VALUE(NetlinkBoundSocket(NETLINK_ROUTE));
   209  
   210    struct request {
   211      struct nlmsghdr hdr;
   212      struct ifinfomsg ifm;
   213    };
   214  
   215    struct request req = {};
   216    req.hdr.nlmsg_len = sizeof(req);
   217    req.hdr.nlmsg_type = RTM_NEWLINK;
   218    req.hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK;
   219    req.hdr.nlmsg_seq = kSeq;
   220    req.ifm.ifi_family = AF_UNSPEC;
   221    req.ifm.ifi_index = loopback_link.index;
   222    req.ifm.ifi_change = IFF_UP;
   223    req.ifm.ifi_flags = IFF_UP;
   224    EXPECT_NO_ERRNO(NetlinkRequestAckOrError(fd, kSeq, &req, sizeof(req)));
   225  }
   226  
   227  TEST(NetlinkRouteTest, GetLinkByName) {
   228    Link loopback_link = ASSERT_NO_ERRNO_AND_VALUE(LoopbackLink());
   229  
   230    FileDescriptor fd =
   231        ASSERT_NO_ERRNO_AND_VALUE(NetlinkBoundSocket(NETLINK_ROUTE));
   232  
   233    struct request {
   234      struct nlmsghdr hdr;
   235      struct ifinfomsg ifm;
   236      struct rtattr rtattr;
   237      char ifname[IFNAMSIZ];
   238      char pad[NLMSG_ALIGNTO + RTA_ALIGNTO];
   239    };
   240  
   241    struct request req = {};
   242    req.hdr.nlmsg_type = RTM_GETLINK;
   243    req.hdr.nlmsg_flags = NLM_F_REQUEST;
   244    req.hdr.nlmsg_seq = kSeq;
   245    req.ifm.ifi_family = AF_UNSPEC;
   246    req.rtattr.rta_type = IFLA_IFNAME;
   247    req.rtattr.rta_len = RTA_LENGTH(loopback_link.name.size() + 1);
   248    strncpy(req.ifname, loopback_link.name.c_str(), sizeof(req.ifname));
   249    req.hdr.nlmsg_len =
   250        NLMSG_LENGTH(sizeof(req.ifm)) + NLMSG_ALIGN(req.rtattr.rta_len);
   251  
   252    bool found = false;
   253    ASSERT_NO_ERRNO(NetlinkRequestResponse(
   254        fd, &req, sizeof(req),
   255        [&](const struct nlmsghdr* hdr) {
   256          CheckLinkMsg(hdr, loopback_link);
   257          found = true;
   258        },
   259        false));
   260    EXPECT_TRUE(found) << "Netlink response does not contain any links.";
   261  }
   262  
   263  TEST(NetlinkRouteTest, GetLinkByIndexNotFound) {
   264    FileDescriptor fd =
   265        ASSERT_NO_ERRNO_AND_VALUE(NetlinkBoundSocket(NETLINK_ROUTE));
   266  
   267    struct request {
   268      struct nlmsghdr hdr;
   269      struct ifinfomsg ifm;
   270    };
   271  
   272    struct request req = {};
   273    req.hdr.nlmsg_len = sizeof(req);
   274    req.hdr.nlmsg_type = RTM_GETLINK;
   275    req.hdr.nlmsg_flags = NLM_F_REQUEST;
   276    req.hdr.nlmsg_seq = kSeq;
   277    req.ifm.ifi_family = AF_UNSPEC;
   278    req.ifm.ifi_index = 1234590;
   279  
   280    EXPECT_THAT(NetlinkRequestAckOrError(fd, kSeq, &req, sizeof(req)),
   281                PosixErrorIs(ENODEV, _));
   282  }
   283  
   284  TEST(NetlinkRouteTest, GetLinkByNameNotFound) {
   285    const std::string name = "nodevice?!";
   286  
   287    FileDescriptor fd =
   288        ASSERT_NO_ERRNO_AND_VALUE(NetlinkBoundSocket(NETLINK_ROUTE));
   289  
   290    struct request {
   291      struct nlmsghdr hdr;
   292      struct ifinfomsg ifm;
   293      struct rtattr rtattr;
   294      char ifname[IFNAMSIZ];
   295      char pad[NLMSG_ALIGNTO + RTA_ALIGNTO];
   296    };
   297  
   298    struct request req = {};
   299    req.hdr.nlmsg_type = RTM_GETLINK;
   300    req.hdr.nlmsg_flags = NLM_F_REQUEST;
   301    req.hdr.nlmsg_seq = kSeq;
   302    req.ifm.ifi_family = AF_UNSPEC;
   303    req.rtattr.rta_type = IFLA_IFNAME;
   304    req.rtattr.rta_len = RTA_LENGTH(name.size() + 1);
   305    strncpy(req.ifname, name.c_str(), sizeof(req.ifname));
   306    req.hdr.nlmsg_len =
   307        NLMSG_LENGTH(sizeof(req.ifm)) + NLMSG_ALIGN(req.rtattr.rta_len);
   308  
   309    EXPECT_THAT(NetlinkRequestAckOrError(fd, kSeq, &req, sizeof(req)),
   310                PosixErrorIs(ENODEV, _));
   311  }
   312  
   313  TEST(NetlinkRouteTest, RemoveLoopbackByName) {
   314    SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_ADMIN)));
   315    Link loopback_link = ASSERT_NO_ERRNO_AND_VALUE(LoopbackLink());
   316  
   317    FileDescriptor fd =
   318        ASSERT_NO_ERRNO_AND_VALUE(NetlinkBoundSocket(NETLINK_ROUTE));
   319  
   320    struct request {
   321      struct nlmsghdr hdr;
   322      struct ifinfomsg ifm;
   323      struct rtattr rtattr;
   324      char ifname[IFNAMSIZ];
   325      char pad[NLMSG_ALIGNTO + RTA_ALIGNTO];
   326    };
   327  
   328    struct request req = {};
   329    req.hdr.nlmsg_type = RTM_DELLINK;
   330    req.hdr.nlmsg_flags = NLM_F_REQUEST;
   331    req.hdr.nlmsg_seq = kSeq;
   332    req.ifm.ifi_family = AF_UNSPEC;
   333    req.rtattr.rta_type = IFLA_IFNAME;
   334    req.rtattr.rta_len = RTA_LENGTH(loopback_link.name.size() + 1);
   335    strncpy(req.ifname, loopback_link.name.c_str(), sizeof(req.ifname));
   336    req.hdr.nlmsg_len =
   337        NLMSG_LENGTH(sizeof(req.ifm)) + NLMSG_ALIGN(req.rtattr.rta_len);
   338  
   339    EXPECT_THAT(NetlinkRequestAckOrError(fd, kSeq, &req, sizeof(req)),
   340                PosixErrorIs(ENOTSUP, _));
   341  }
   342  
   343  TEST(NetlinkRouteTest, RemoveLoopbackByIndex) {
   344    SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_ADMIN)));
   345    Link loopback_link = ASSERT_NO_ERRNO_AND_VALUE(LoopbackLink());
   346    FileDescriptor fd =
   347        ASSERT_NO_ERRNO_AND_VALUE(NetlinkBoundSocket(NETLINK_ROUTE));
   348  
   349    struct request {
   350      struct nlmsghdr hdr;
   351      struct ifinfomsg ifm;
   352    };
   353  
   354    struct request req = {};
   355    req.hdr.nlmsg_len = sizeof(req);
   356    req.hdr.nlmsg_type = RTM_DELLINK;
   357    req.hdr.nlmsg_flags = NLM_F_REQUEST;
   358    req.hdr.nlmsg_seq = kSeq;
   359    req.ifm.ifi_family = AF_UNSPEC;
   360    req.ifm.ifi_index = loopback_link.index;
   361  
   362    EXPECT_THAT(NetlinkRequestAckOrError(fd, kSeq, &req, sizeof(req)),
   363                PosixErrorIs(ENOTSUP, _));
   364  }
   365  
   366  TEST(NetlinkRouteTest, RemoveLinkByIndexNotFound) {
   367    SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_ADMIN)));
   368    FileDescriptor fd =
   369        ASSERT_NO_ERRNO_AND_VALUE(NetlinkBoundSocket(NETLINK_ROUTE));
   370  
   371    struct request {
   372      struct nlmsghdr hdr;
   373      struct ifinfomsg ifm;
   374    };
   375  
   376    struct request req = {};
   377    req.hdr.nlmsg_len = sizeof(req);
   378    req.hdr.nlmsg_type = RTM_GETLINK;
   379    req.hdr.nlmsg_flags = NLM_F_REQUEST;
   380    req.hdr.nlmsg_seq = kSeq;
   381    req.ifm.ifi_family = AF_UNSPEC;
   382    req.ifm.ifi_index = 1234590;
   383  
   384    EXPECT_THAT(NetlinkRequestAckOrError(fd, kSeq, &req, sizeof(req)),
   385                PosixErrorIs(ENODEV, _));
   386  }
   387  
   388  TEST(NetlinkRouteTest, RemoveLinkByNameNotFound) {
   389    const std::string name = "nodevice?!";
   390  
   391    SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_ADMIN)));
   392    FileDescriptor fd =
   393        ASSERT_NO_ERRNO_AND_VALUE(NetlinkBoundSocket(NETLINK_ROUTE));
   394  
   395    struct request {
   396      struct nlmsghdr hdr;
   397      struct ifinfomsg ifm;
   398      struct rtattr rtattr;
   399      char ifname[IFNAMSIZ];
   400      char pad[NLMSG_ALIGNTO + RTA_ALIGNTO];
   401    };
   402  
   403    struct request req = {};
   404    req.hdr.nlmsg_type = RTM_DELLINK;
   405    req.hdr.nlmsg_flags = NLM_F_REQUEST;
   406    req.hdr.nlmsg_seq = kSeq;
   407    req.ifm.ifi_family = AF_UNSPEC;
   408    req.rtattr.rta_type = IFLA_IFNAME;
   409    req.rtattr.rta_len = RTA_LENGTH(name.size() + 1);
   410    strncpy(req.ifname, name.c_str(), sizeof(req.ifname));
   411    req.hdr.nlmsg_len =
   412        NLMSG_LENGTH(sizeof(req.ifm)) + NLMSG_ALIGN(req.rtattr.rta_len);
   413  
   414    EXPECT_THAT(NetlinkRequestAckOrError(fd, kSeq, &req, sizeof(req)),
   415                PosixErrorIs(ENODEV, _));
   416  }
   417  
   418  TEST(NetlinkRouteTest, MsgHdrMsgUnsuppType) {
   419    FileDescriptor fd =
   420        ASSERT_NO_ERRNO_AND_VALUE(NetlinkBoundSocket(NETLINK_ROUTE));
   421  
   422    struct request {
   423      struct nlmsghdr hdr;
   424      struct ifinfomsg ifm;
   425    };
   426  
   427    struct request req = {};
   428    req.hdr.nlmsg_len = sizeof(req);
   429    // If type & 0x3 is equal to 0x2, this means a get request
   430    // which doesn't require CAP_SYS_ADMIN.
   431    req.hdr.nlmsg_type = ((__RTM_MAX + 1024) & (~0x3)) | 0x2;
   432    req.hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
   433    req.hdr.nlmsg_seq = kSeq;
   434    req.ifm.ifi_family = AF_UNSPEC;
   435  
   436    EXPECT_THAT(NetlinkRequestAckOrError(fd, kSeq, &req, sizeof(req)),
   437                PosixErrorIs(EOPNOTSUPP, _));
   438  }
   439  
   440  TEST(NetlinkRouteTest, MsgHdrMsgTrunc) {
   441    FileDescriptor fd =
   442        ASSERT_NO_ERRNO_AND_VALUE(NetlinkBoundSocket(NETLINK_ROUTE));
   443  
   444    struct request {
   445      struct nlmsghdr hdr;
   446      struct ifinfomsg ifm;
   447    };
   448  
   449    struct request req = {};
   450    req.hdr.nlmsg_len = sizeof(req);
   451    req.hdr.nlmsg_type = RTM_GETLINK;
   452    req.hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
   453    req.hdr.nlmsg_seq = kSeq;
   454    req.ifm.ifi_family = AF_UNSPEC;
   455  
   456    struct iovec iov = {};
   457    iov.iov_base = &req;
   458    iov.iov_len = sizeof(req);
   459  
   460    struct msghdr msg = {};
   461    msg.msg_iov = &iov;
   462    msg.msg_iovlen = 1;
   463    // No destination required; it defaults to pid 0, the kernel.
   464  
   465    ASSERT_THAT(RetryEINTR(sendmsg)(fd.get(), &msg, 0), SyscallSucceeds());
   466  
   467    // Small enough to ensure that the response doesn't fit.
   468    constexpr size_t kBufferSize = 10;
   469    std::vector<char> buf(kBufferSize);
   470    iov.iov_base = buf.data();
   471    iov.iov_len = buf.size();
   472  
   473    ASSERT_THAT(RetryEINTR(recvmsg)(fd.get(), &msg, 0),
   474                SyscallSucceedsWithValue(kBufferSize));
   475    EXPECT_EQ((msg.msg_flags & MSG_TRUNC), MSG_TRUNC);
   476  }
   477  
   478  TEST(NetlinkRouteTest, SpliceFromPipe) {
   479    Link loopback_link = ASSERT_NO_ERRNO_AND_VALUE(LoopbackLink());
   480    FileDescriptor fd =
   481        ASSERT_NO_ERRNO_AND_VALUE(NetlinkBoundSocket(NETLINK_ROUTE));
   482  
   483    int fds[2];
   484    ASSERT_THAT(pipe(fds), SyscallSucceeds());
   485    FileDescriptor rfd(fds[0]);
   486    FileDescriptor wfd(fds[1]);
   487  
   488    struct request {
   489      struct nlmsghdr hdr;
   490      struct ifinfomsg ifm;
   491    };
   492  
   493    struct request req = {};
   494    req.hdr.nlmsg_len = sizeof(req);
   495    req.hdr.nlmsg_type = RTM_GETLINK;
   496    req.hdr.nlmsg_flags = NLM_F_REQUEST;
   497    req.hdr.nlmsg_seq = kSeq;
   498    req.ifm.ifi_family = AF_UNSPEC;
   499    req.ifm.ifi_index = loopback_link.index;
   500  
   501    ASSERT_THAT(write(wfd.get(), &req, sizeof(req)),
   502                SyscallSucceedsWithValue(sizeof(req)));
   503  
   504    EXPECT_THAT(splice(rfd.get(), nullptr, fd.get(), nullptr, sizeof(req) + 1, 0),
   505                SyscallSucceedsWithValue(sizeof(req)));
   506    close(wfd.release());
   507    EXPECT_THAT(splice(rfd.get(), nullptr, fd.get(), nullptr, sizeof(req) + 1, 0),
   508                SyscallSucceedsWithValue(0));
   509  
   510    bool found = false;
   511    ASSERT_NO_ERRNO(NetlinkResponse(
   512        fd,
   513        [&](const struct nlmsghdr* hdr) {
   514          CheckLinkMsg(hdr, loopback_link);
   515          found = true;
   516        },
   517        false));
   518    EXPECT_TRUE(found) << "Netlink response does not contain any links.";
   519  }
   520  
   521  TEST(NetlinkRouteTest, MsgTruncMsgHdrMsgTrunc) {
   522    FileDescriptor fd =
   523        ASSERT_NO_ERRNO_AND_VALUE(NetlinkBoundSocket(NETLINK_ROUTE));
   524  
   525    struct request {
   526      struct nlmsghdr hdr;
   527      struct ifinfomsg ifm;
   528    };
   529  
   530    struct request req = {};
   531    req.hdr.nlmsg_len = sizeof(req);
   532    req.hdr.nlmsg_type = RTM_GETLINK;
   533    req.hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
   534    req.hdr.nlmsg_seq = kSeq;
   535    req.ifm.ifi_family = AF_UNSPEC;
   536  
   537    struct iovec iov = {};
   538    iov.iov_base = &req;
   539    iov.iov_len = sizeof(req);
   540  
   541    struct msghdr msg = {};
   542    msg.msg_iov = &iov;
   543    msg.msg_iovlen = 1;
   544    // No destination required; it defaults to pid 0, the kernel.
   545  
   546    ASSERT_THAT(RetryEINTR(sendmsg)(fd.get(), &msg, 0), SyscallSucceeds());
   547  
   548    // Small enough to ensure that the response doesn't fit.
   549    constexpr size_t kBufferSize = 10;
   550    std::vector<char> buf(kBufferSize);
   551    iov.iov_base = buf.data();
   552    iov.iov_len = buf.size();
   553  
   554    int res = 0;
   555    ASSERT_THAT(res = RetryEINTR(recvmsg)(fd.get(), &msg, MSG_TRUNC),
   556                SyscallSucceeds());
   557    EXPECT_GT(res, kBufferSize);
   558    EXPECT_EQ((msg.msg_flags & MSG_TRUNC), MSG_TRUNC);
   559  }
   560  
   561  TEST(NetlinkRouteTest, ControlMessageIgnored) {
   562    FileDescriptor fd =
   563        ASSERT_NO_ERRNO_AND_VALUE(NetlinkBoundSocket(NETLINK_ROUTE));
   564    uint32_t port = ASSERT_NO_ERRNO_AND_VALUE(NetlinkPortID(fd.get()));
   565  
   566    struct request {
   567      struct nlmsghdr control_hdr;
   568      struct nlmsghdr message_hdr;
   569      struct ifinfomsg ifm;
   570    };
   571  
   572    struct request req = {};
   573  
   574    // This control message is ignored. We still receive a response for the
   575    // following RTM_GETLINK.
   576    req.control_hdr.nlmsg_len = sizeof(req.control_hdr);
   577    req.control_hdr.nlmsg_type = NLMSG_DONE;
   578    req.control_hdr.nlmsg_seq = kSeq;
   579  
   580    req.message_hdr.nlmsg_len = sizeof(req.message_hdr) + sizeof(req.ifm);
   581    req.message_hdr.nlmsg_type = RTM_GETLINK;
   582    req.message_hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
   583    req.message_hdr.nlmsg_seq = kSeq;
   584  
   585    req.ifm.ifi_family = AF_UNSPEC;
   586  
   587    ASSERT_NO_ERRNO(NetlinkRequestResponse(
   588        fd, &req, sizeof(req),
   589        [&](const struct nlmsghdr* hdr) {
   590          CheckGetLinkResponse(hdr, kSeq, port);
   591        },
   592        false));
   593  }
   594  
   595  TEST(NetlinkRouteTest, GetAddrDump) {
   596    FileDescriptor fd =
   597        ASSERT_NO_ERRNO_AND_VALUE(NetlinkBoundSocket(NETLINK_ROUTE));
   598    uint32_t port = ASSERT_NO_ERRNO_AND_VALUE(NetlinkPortID(fd.get()));
   599  
   600    struct request {
   601      struct nlmsghdr hdr;
   602      struct rtgenmsg rgm;
   603    };
   604  
   605    struct request req;
   606    req.hdr.nlmsg_len = sizeof(req);
   607    req.hdr.nlmsg_type = RTM_GETADDR;
   608    req.hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
   609    req.hdr.nlmsg_seq = kSeq;
   610    req.rgm.rtgen_family = AF_UNSPEC;
   611  
   612    ASSERT_NO_ERRNO(NetlinkRequestResponse(
   613        fd, &req, sizeof(req),
   614        [&](const struct nlmsghdr* hdr) {
   615          EXPECT_THAT(hdr->nlmsg_type, AnyOf(Eq(RTM_NEWADDR), Eq(NLMSG_DONE)));
   616  
   617          EXPECT_TRUE((hdr->nlmsg_flags & NLM_F_MULTI) == NLM_F_MULTI)
   618              << std::hex << hdr->nlmsg_flags;
   619  
   620          EXPECT_EQ(hdr->nlmsg_seq, kSeq);
   621          EXPECT_EQ(hdr->nlmsg_pid, port);
   622  
   623          if (hdr->nlmsg_type != RTM_NEWADDR) {
   624            return;
   625          }
   626  
   627          // RTM_NEWADDR contains at least the header and ifaddrmsg.
   628          EXPECT_GE(hdr->nlmsg_len, sizeof(*hdr) + sizeof(struct ifaddrmsg));
   629  
   630          // TODO(mpratt): Check ifaddrmsg contents and following attrs.
   631        },
   632        false));
   633  }
   634  
   635  TEST(NetlinkRouteTest, LookupAll) {
   636    struct ifaddrs* if_addr_list = nullptr;
   637    auto cleanup = Cleanup([&if_addr_list]() { freeifaddrs(if_addr_list); });
   638  
   639    // Not a syscall but we can use the syscall matcher as glibc sets errno.
   640    ASSERT_THAT(getifaddrs(&if_addr_list), SyscallSucceeds());
   641  
   642    int count = 0;
   643    for (struct ifaddrs* i = if_addr_list; i; i = i->ifa_next) {
   644      if (!i->ifa_addr || (i->ifa_addr->sa_family != AF_INET &&
   645                           i->ifa_addr->sa_family != AF_INET6)) {
   646        continue;
   647      }
   648      count++;
   649    }
   650    ASSERT_GT(count, 0);
   651  }
   652  
   653  TEST(NetlinkRouteTest, AddAndRemoveAddr) {
   654    SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_ADMIN)));
   655    // Don't do cooperative save/restore because netstack state is not restored.
   656    // TODO(gvisor.dev/issue/4595): enable cooperative save tests.
   657    const DisableSave ds;
   658  
   659    Link loopback_link = ASSERT_NO_ERRNO_AND_VALUE(LoopbackLink());
   660  
   661    struct in_addr addr;
   662    ASSERT_EQ(inet_pton(AF_INET, "10.0.0.1", &addr), 1);
   663  
   664    // Create should succeed, as no such address in kernel.
   665    ASSERT_NO_ERRNO(LinkAddLocalAddr(loopback_link.index, AF_INET,
   666                                     /*prefixlen=*/24, &addr, sizeof(addr)));
   667  
   668    Cleanup defer_addr_removal = Cleanup(
   669        [loopback_link = std::move(loopback_link), addr = std::move(addr)] {
   670          // First delete should succeed, as address exists.
   671          EXPECT_NO_ERRNO(LinkDelLocalAddr(loopback_link.index, AF_INET,
   672                                           /*prefixlen=*/24, &addr,
   673                                           sizeof(addr)));
   674  
   675          // Second delete should fail, as address no longer exists.
   676          EXPECT_THAT(LinkDelLocalAddr(loopback_link.index, AF_INET,
   677                                       /*prefixlen=*/24, &addr, sizeof(addr)),
   678                      PosixErrorIs(EADDRNOTAVAIL, _));
   679        });
   680  
   681    // Replace an existing address should succeed.
   682    ASSERT_NO_ERRNO(LinkReplaceLocalAddr(loopback_link.index, AF_INET,
   683                                         /*prefixlen=*/24, &addr, sizeof(addr)));
   684  
   685    // Create exclusive should fail, as we created the address above.
   686    EXPECT_THAT(LinkAddExclusiveLocalAddr(loopback_link.index, AF_INET,
   687                                          /*prefixlen=*/24, &addr, sizeof(addr)),
   688                PosixErrorIs(EEXIST, _));
   689  }
   690  
   691  // GetRouteDump tests a RTM_GETROUTE + NLM_F_DUMP request.
   692  TEST(NetlinkRouteTest, GetRouteDump) {
   693    FileDescriptor fd =
   694        ASSERT_NO_ERRNO_AND_VALUE(NetlinkBoundSocket(NETLINK_ROUTE));
   695    uint32_t port = ASSERT_NO_ERRNO_AND_VALUE(NetlinkPortID(fd.get()));
   696  
   697    struct request {
   698      struct nlmsghdr hdr;
   699      struct rtmsg rtm;
   700    };
   701  
   702    struct request req = {};
   703    req.hdr.nlmsg_len = sizeof(req);
   704    req.hdr.nlmsg_type = RTM_GETROUTE;
   705    req.hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
   706    req.hdr.nlmsg_seq = kSeq;
   707    req.rtm.rtm_family = AF_UNSPEC;
   708  
   709    bool routeFound = false;
   710    bool dstFound = true;
   711    bool defaultRouteDstFound = false;
   712    ASSERT_NO_ERRNO(NetlinkRequestResponse(
   713        fd, &req, sizeof(req),
   714        [&](const struct nlmsghdr* hdr) {
   715          // Validate the reponse to RTM_GETROUTE + NLM_F_DUMP.
   716          EXPECT_THAT(hdr->nlmsg_type, AnyOf(Eq(RTM_NEWROUTE), Eq(NLMSG_DONE)));
   717  
   718          EXPECT_TRUE((hdr->nlmsg_flags & NLM_F_MULTI) == NLM_F_MULTI)
   719              << std::hex << hdr->nlmsg_flags;
   720  
   721          EXPECT_EQ(hdr->nlmsg_seq, kSeq);
   722          EXPECT_EQ(hdr->nlmsg_pid, port);
   723  
   724          // The test should not proceed if it's not a RTM_NEWROUTE message.
   725          if (hdr->nlmsg_type != RTM_NEWROUTE) {
   726            return;
   727          }
   728  
   729          // RTM_NEWROUTE contains at least the header and rtmsg.
   730          ASSERT_GE(hdr->nlmsg_len, NLMSG_SPACE(sizeof(struct rtmsg)));
   731          const struct rtmsg* msg =
   732              reinterpret_cast<const struct rtmsg*>(NLMSG_DATA(hdr));
   733          // NOTE: rtmsg fields are char fields.
   734          std::cout << "Found route table=" << static_cast<int>(msg->rtm_table)
   735                    << ", protocol=" << static_cast<int>(msg->rtm_protocol)
   736                    << ", scope=" << static_cast<int>(msg->rtm_scope)
   737                    << ", type=" << static_cast<int>(msg->rtm_type)
   738                    << ", prefixlen=" << static_cast<int>(msg->rtm_dst_len);
   739  
   740          int len = RTM_PAYLOAD(hdr);
   741          bool rtDstFound = false;
   742          for (struct rtattr* attr = RTM_RTA(msg); RTA_OK(attr, len);
   743               attr = RTA_NEXT(attr, len)) {
   744            if (attr->rta_type == RTA_DST) {
   745              char address[INET_ADDRSTRLEN] = {};
   746              inet_ntop(AF_INET, RTA_DATA(attr), address, sizeof(address));
   747              std::cout << ", dst=" << address;
   748              rtDstFound = true;
   749            }
   750          }
   751  
   752          std::cout << std::endl;
   753  
   754          // If the test is running in a new network namespace, it will have only
   755          // the local route table.
   756          if (msg->rtm_table == RT_TABLE_MAIN ||
   757              (!IsRunningOnGvisor() && msg->rtm_table == RT_TABLE_LOCAL)) {
   758            routeFound = true;
   759            if (msg->rtm_dst_len) {
   760              dstFound = rtDstFound && dstFound;
   761            } else {
   762              defaultRouteDstFound = rtDstFound || defaultRouteDstFound;
   763            }
   764          }
   765        },
   766        false));
   767    // At least one route found in main route table.
   768    EXPECT_TRUE(routeFound);
   769    // Found RTA_DST for each route in main table.
   770    EXPECT_TRUE(dstFound);
   771    // RTA_DST should not be present for default routes.
   772    EXPECT_FALSE(defaultRouteDstFound);
   773  }
   774  
   775  // GetRouteRequest tests a RTM_GETROUTE request with RTM_F_LOOKUP_TABLE flag.
   776  TEST(NetlinkRouteTest, GetRouteRequest) {
   777    FileDescriptor fd =
   778        ASSERT_NO_ERRNO_AND_VALUE(NetlinkBoundSocket(NETLINK_ROUTE));
   779    uint32_t port = ASSERT_NO_ERRNO_AND_VALUE(NetlinkPortID(fd.get()));
   780  
   781    struct request {
   782      struct nlmsghdr hdr;
   783      struct rtmsg rtm;
   784      struct nlattr nla;
   785      struct in_addr sin_addr;
   786    };
   787  
   788    constexpr uint32_t kSeq = 12345;
   789  
   790    struct request req = {};
   791    req.hdr.nlmsg_len = sizeof(req);
   792    req.hdr.nlmsg_type = RTM_GETROUTE;
   793    req.hdr.nlmsg_flags = NLM_F_REQUEST;
   794    req.hdr.nlmsg_seq = kSeq;
   795  
   796    req.rtm.rtm_family = AF_INET;
   797    req.rtm.rtm_dst_len = 32;
   798    req.rtm.rtm_src_len = 0;
   799    req.rtm.rtm_tos = 0;
   800    req.rtm.rtm_table = RT_TABLE_UNSPEC;
   801    req.rtm.rtm_protocol = RTPROT_UNSPEC;
   802    req.rtm.rtm_scope = RT_SCOPE_UNIVERSE;
   803    req.rtm.rtm_type = RTN_UNSPEC;
   804    req.rtm.rtm_flags = RTM_F_LOOKUP_TABLE;
   805  
   806    req.nla.nla_len = 8;
   807    req.nla.nla_type = RTA_DST;
   808    inet_aton("127.0.0.2", &req.sin_addr);
   809  
   810    bool rtDstFound = false;
   811    ASSERT_NO_ERRNO(NetlinkRequestResponseSingle(
   812        fd, &req, sizeof(req), [&](const struct nlmsghdr* hdr) {
   813          // Validate the reponse to RTM_GETROUTE request with RTM_F_LOOKUP_TABLE
   814          // flag.
   815          EXPECT_THAT(hdr->nlmsg_type, RTM_NEWROUTE);
   816  
   817          EXPECT_TRUE(hdr->nlmsg_flags == 0) << std::hex << hdr->nlmsg_flags;
   818  
   819          EXPECT_EQ(hdr->nlmsg_seq, kSeq);
   820          EXPECT_EQ(hdr->nlmsg_pid, port);
   821  
   822          // RTM_NEWROUTE contains at least the header and rtmsg.
   823          ASSERT_GE(hdr->nlmsg_len, NLMSG_SPACE(sizeof(struct rtmsg)));
   824          const struct rtmsg* msg =
   825              reinterpret_cast<const struct rtmsg*>(NLMSG_DATA(hdr));
   826  
   827          // NOTE: rtmsg fields are char fields.
   828          std::cout << "Found route table=" << static_cast<int>(msg->rtm_table)
   829                    << ", protocol=" << static_cast<int>(msg->rtm_protocol)
   830                    << ", scope=" << static_cast<int>(msg->rtm_scope)
   831                    << ", type=" << static_cast<int>(msg->rtm_type);
   832  
   833          EXPECT_EQ(msg->rtm_family, AF_INET);
   834          EXPECT_EQ(msg->rtm_dst_len, 32);
   835          EXPECT_TRUE((msg->rtm_flags & RTM_F_CLONED) == RTM_F_CLONED)
   836              << std::hex << msg->rtm_flags;
   837  
   838          int len = RTM_PAYLOAD(hdr);
   839          std::cout << ", len=" << len;
   840          for (struct rtattr* attr = RTM_RTA(msg); RTA_OK(attr, len);
   841               attr = RTA_NEXT(attr, len)) {
   842            if (attr->rta_type == RTA_DST) {
   843              char address[INET_ADDRSTRLEN] = {};
   844              inet_ntop(AF_INET, RTA_DATA(attr), address, sizeof(address));
   845              std::cout << ", dst=" << address;
   846              rtDstFound = true;
   847            } else if (attr->rta_type == RTA_OIF) {
   848              const char* oif = reinterpret_cast<const char*>(RTA_DATA(attr));
   849              std::cout << ", oif=" << oif;
   850            }
   851          }
   852  
   853          std::cout << std::endl;
   854        }));
   855    // Found RTA_DST for RTM_F_LOOKUP_TABLE.
   856    EXPECT_TRUE(rtDstFound);
   857  }
   858  
   859  // NetlinkRouteTest with a single parameter that must be AF_INET or AF_INET6.
   860  using NetlinkRouteIpInvariantTest = ::testing::TestWithParam<int>;
   861  
   862  INSTANTIATE_TEST_SUITE_P(NetlinkRouteIpv4AndIpv6Tests,
   863                           NetlinkRouteIpInvariantTest,
   864                           ::testing::Values(AF_INET, AF_INET6));
   865  
   866  TEST_P(NetlinkRouteIpInvariantTest, AddAndRemoveRoute) {
   867    // Gvisor does not support `RTM_NEWROUTE` or `RTM_DELROUTE`.
   868    SKIP_IF(IsRunningOnGvisor() && GvisorPlatform() != Platform::kStarnix);
   869    // CAP_NET_ADMIN is required to modify the routing table.
   870    SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_ADMIN)));
   871  
   872    // Based on the test parameter, build an IPv4 or IPv6 destination subnet.
   873    int family = GetParam();
   874    struct in_addr dst_v4;
   875    struct in6_addr dst_v6;
   876    void* dst = nullptr;
   877    int dst_len;
   878    int prefixlen;
   879    switch (family) {
   880      case AF_INET:
   881        ASSERT_EQ(inet_pton(family, "192.0.2.0", &dst_v4), 1);
   882        prefixlen = 24;
   883        dst = &dst_v4;
   884        dst_len = sizeof(dst_v4);
   885        break;
   886      case AF_INET6:
   887        ASSERT_EQ(inet_pton(family, "2001:db8::", &dst_v6), 1);
   888        prefixlen = 64;
   889        dst = &dst_v6;
   890        dst_len = sizeof(dst_v6);
   891        break;
   892      default:
   893        FAIL() << "address family must be AF_INET or AF_INET6";
   894    }
   895  
   896    Link loopback_link = ASSERT_NO_ERRNO_AND_VALUE(LoopbackLink());
   897  
   898    // Create should succeed, as no such route in kernel.
   899    ASSERT_NO_ERRNO(
   900        AddUnicastRoute(loopback_link.index, family, prefixlen, dst, dst_len));
   901  
   902    // Second create should fail, as we already created the route above.
   903    EXPECT_THAT(
   904        AddUnicastRoute(loopback_link.index, family, prefixlen, dst, dst_len),
   905        PosixErrorIs(EEXIST, _));
   906  
   907    // First delete should succeed, as route exists.
   908    EXPECT_NO_ERRNO(
   909        DelUnicastRoute(loopback_link.index, family, prefixlen, dst, dst_len));
   910  
   911    // Second delete should fail, as route no longer exists.
   912    EXPECT_THAT(
   913        DelUnicastRoute(loopback_link.index, family, prefixlen, dst, dst_len),
   914        PosixErrorIs(ESRCH, _));
   915  }
   916  
   917  // GetRuleDump tests a RTM_GETRULE + NLM_F_DUMP request.
   918  TEST(NetlinkRouteTest, GetRuleDump) {
   919    // Gvisor does not support `RTM_GETRULE`
   920    SKIP_IF(IsRunningOnGvisor() && GvisorPlatform() != Platform::kStarnix);
   921  
   922    FileDescriptor fd =
   923        ASSERT_NO_ERRNO_AND_VALUE(NetlinkBoundSocket(NETLINK_ROUTE));
   924    uint32_t port = ASSERT_NO_ERRNO_AND_VALUE(NetlinkPortID(fd.get()));
   925  
   926    struct request {
   927      struct nlmsghdr hdr;
   928      struct rtmsg rtm;
   929    };
   930  
   931    struct request req = {};
   932    req.hdr.nlmsg_len = sizeof(req);
   933    req.hdr.nlmsg_type = RTM_GETRULE;
   934    req.hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
   935    req.hdr.nlmsg_seq = kSeq;
   936    req.rtm.rtm_family = AF_UNSPEC;
   937  
   938    bool ruleFound = false;
   939    ASSERT_NO_ERRNO(NetlinkRequestResponse(
   940        fd, &req, sizeof(req),
   941        [&](const struct nlmsghdr* hdr) {
   942          // Validate the response to RTM_GETRULE + NLM_F_DUMP.
   943          EXPECT_THAT(hdr->nlmsg_type, AnyOf(Eq(RTM_NEWRULE), Eq(NLMSG_DONE)));
   944  
   945          EXPECT_TRUE((hdr->nlmsg_flags & NLM_F_MULTI) == NLM_F_MULTI)
   946              << std::hex << hdr->nlmsg_flags;
   947  
   948          EXPECT_EQ(hdr->nlmsg_seq, kSeq);
   949          EXPECT_EQ(hdr->nlmsg_pid, port);
   950  
   951          // The test should not proceed if the multipart message is done.
   952          if (hdr->nlmsg_type == NLMSG_DONE) {
   953            return;
   954          }
   955  
   956          // RTM_NEWRULE contains at least the header and rule.
   957          ASSERT_GE(hdr->nlmsg_len, NLMSG_SPACE(sizeof(struct fib_rule_hdr)));
   958          const struct fib_rule_hdr* rule =
   959              reinterpret_cast<const struct fib_rule_hdr*>(NLMSG_DATA(hdr));
   960          std::cout << std::dec << "Found rule"
   961                    << ": family=" << static_cast<int>(rule->family)
   962                    << ", table=" << static_cast<int>(rule->table)
   963                    << ", action=" << static_cast<int>(rule->action) << std::endl;
   964          // All rules should have a non-zero action.
   965          EXPECT_NE(rule->action, 0);
   966          ruleFound = true;
   967        },
   968        false));
   969    // At least one rule found.
   970    EXPECT_TRUE(ruleFound);
   971  }
   972  
   973  TEST_P(NetlinkRouteIpInvariantTest, AddAndRemoveRule) {
   974    // Gvisor does not support `RTM_NEWRULE` or `RTM_DELRULE`.
   975    SKIP_IF(IsRunningOnGvisor() && GvisorPlatform() != Platform::kStarnix);
   976    // CAP_NET_ADMIN is required to modify the rule table.
   977    SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_ADMIN)));
   978  
   979    // Based on the test parameter, build an IPv4 or IPv6 destination subnet.
   980    int family = GetParam();
   981    struct in_addr dst_v4;
   982    struct in6_addr dst_v6;
   983    void* dst = nullptr;
   984    int dst_len;
   985    int prefixlen;
   986    switch (family) {
   987      case AF_INET:
   988        ASSERT_EQ(inet_pton(family, "192.0.2.0", &dst_v4), 1);
   989        prefixlen = 24;
   990        dst = &dst_v4;
   991        dst_len = sizeof(dst_v4);
   992        break;
   993      case AF_INET6:
   994        ASSERT_EQ(inet_pton(family, "2001:db8::", &dst_v6), 1);
   995        prefixlen = 64;
   996        dst = &dst_v6;
   997        dst_len = sizeof(dst_v6);
   998        break;
   999      default:
  1000        FAIL() << "address family must be AF_INET or AF_INET6";
  1001    }
  1002  
  1003    // Unique values for table and priority fields to ensure the new rule does not
  1004    // collide with any of the default rules installed by Linux.
  1005    const int kTable = 99;
  1006    const int kPriority = 12345;
  1007  
  1008    Link loopback_link = ASSERT_NO_ERRNO_AND_VALUE(LoopbackLink());
  1009  
  1010    // Create should succeed, as no such rule in the kernel.
  1011    ASSERT_NO_ERRNO(AddExclusiveLookupInTableRule(family, kTable, kPriority,
  1012                                                  prefixlen, dst, dst_len));
  1013  
  1014    // Second create should fail, as we already created the rule above.
  1015    EXPECT_THAT(AddExclusiveLookupInTableRule(family, kTable, kPriority,
  1016                                              prefixlen, dst, dst_len),
  1017                PosixErrorIs(EEXIST, _));
  1018  
  1019    // First delete should succeed, as rule exists.
  1020    EXPECT_NO_ERRNO(
  1021        DelLookupInTableRule(family, kTable, kPriority, prefixlen, dst, dst_len));
  1022  
  1023    // Second delete should fail, as rule no longer exists.
  1024    EXPECT_THAT(
  1025        DelLookupInTableRule(family, kTable, kPriority, prefixlen, dst, dst_len),
  1026        PosixErrorIs(ENOENT, _));
  1027  }
  1028  
  1029  // RecvmsgTrunc tests the recvmsg MSG_TRUNC flag with zero length output
  1030  // buffer. MSG_TRUNC with a zero length buffer should consume subsequent
  1031  // messages off the socket.
  1032  TEST(NetlinkRouteTest, RecvmsgTrunc) {
  1033    FileDescriptor fd =
  1034        ASSERT_NO_ERRNO_AND_VALUE(NetlinkBoundSocket(NETLINK_ROUTE));
  1035  
  1036    struct request {
  1037      struct nlmsghdr hdr;
  1038      struct rtgenmsg rgm;
  1039    };
  1040  
  1041    struct request req;
  1042    req.hdr.nlmsg_len = sizeof(req);
  1043    req.hdr.nlmsg_type = RTM_GETADDR;
  1044    req.hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
  1045    req.hdr.nlmsg_seq = kSeq;
  1046    req.rgm.rtgen_family = AF_UNSPEC;
  1047  
  1048    struct iovec iov = {};
  1049    iov.iov_base = &req;
  1050    iov.iov_len = sizeof(req);
  1051  
  1052    struct msghdr msg = {};
  1053    msg.msg_iov = &iov;
  1054    msg.msg_iovlen = 1;
  1055  
  1056    ASSERT_THAT(RetryEINTR(sendmsg)(fd.get(), &msg, 0), SyscallSucceeds());
  1057  
  1058    iov.iov_base = NULL;
  1059    iov.iov_len = 0;
  1060  
  1061    int trunclen, trunclen2;
  1062  
  1063    // Note: This test assumes at least two messages are returned by the
  1064    // RTM_GETADDR request. That means at least one RTM_NEWLINK message and one
  1065    // NLMSG_DONE message. We cannot read all the messages without blocking
  1066    // because we would need to read the message into a buffer and check the
  1067    // nlmsg_type for NLMSG_DONE. However, the test depends on reading into a
  1068    // zero-length buffer.
  1069  
  1070    // First, call recvmsg with MSG_TRUNC. This will read the full message from
  1071    // the socket and return it's full length. Subsequent calls to recvmsg will
  1072    // read the next messages from the socket.
  1073    ASSERT_THAT(trunclen = RetryEINTR(recvmsg)(fd.get(), &msg, MSG_TRUNC),
  1074                SyscallSucceeds());
  1075  
  1076    // Message should always be truncated. However, While the destination iov is
  1077    // zero length, MSG_TRUNC returns the size of the next message so it should
  1078    // not be zero.
  1079    ASSERT_EQ(msg.msg_flags & MSG_TRUNC, MSG_TRUNC);
  1080    ASSERT_NE(trunclen, 0);
  1081    // Returned length is at least the header and ifaddrmsg.
  1082    EXPECT_GE(trunclen, sizeof(struct nlmsghdr) + sizeof(struct ifaddrmsg));
  1083  
  1084    // Reset the msg_flags to make sure that the recvmsg call is setting them
  1085    // properly.
  1086    msg.msg_flags = 0;
  1087  
  1088    // Make a second recvvmsg call to get the next message.
  1089    ASSERT_THAT(trunclen2 = RetryEINTR(recvmsg)(fd.get(), &msg, MSG_TRUNC),
  1090                SyscallSucceeds());
  1091    ASSERT_EQ(msg.msg_flags & MSG_TRUNC, MSG_TRUNC);
  1092    ASSERT_NE(trunclen2, 0);
  1093  
  1094    // Assert that the received messages are not the same.
  1095    //
  1096    // We are calling recvmsg with a zero length buffer so we have no way to
  1097    // inspect the messages to make sure they are not equal in value. The best
  1098    // we can do is to compare their lengths.
  1099    ASSERT_NE(trunclen, trunclen2);
  1100  }
  1101  
  1102  // RecvmsgTruncPeek tests recvmsg with the combination of the MSG_TRUNC and
  1103  // MSG_PEEK flags and a zero length output buffer. This is normally used to
  1104  // read the full length of the next message on the socket without consuming
  1105  // it, so a properly sized buffer can be allocated to store the message. This
  1106  // test tests that scenario.
  1107  TEST(NetlinkRouteTest, RecvmsgTruncPeek) {
  1108    FileDescriptor fd =
  1109        ASSERT_NO_ERRNO_AND_VALUE(NetlinkBoundSocket(NETLINK_ROUTE));
  1110  
  1111    struct request {
  1112      struct nlmsghdr hdr;
  1113      struct rtgenmsg rgm;
  1114    };
  1115  
  1116    struct request req;
  1117    req.hdr.nlmsg_len = sizeof(req);
  1118    req.hdr.nlmsg_type = RTM_GETADDR;
  1119    req.hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
  1120    req.hdr.nlmsg_seq = kSeq;
  1121    req.rgm.rtgen_family = AF_UNSPEC;
  1122  
  1123    struct iovec iov = {};
  1124    iov.iov_base = &req;
  1125    iov.iov_len = sizeof(req);
  1126  
  1127    struct msghdr msg = {};
  1128    msg.msg_iov = &iov;
  1129    msg.msg_iovlen = 1;
  1130  
  1131    ASSERT_THAT(RetryEINTR(sendmsg)(fd.get(), &msg, 0), SyscallSucceeds());
  1132  
  1133    int type = -1;
  1134    do {
  1135      int peeklen;
  1136      int len;
  1137  
  1138      iov.iov_base = NULL;
  1139      iov.iov_len = 0;
  1140  
  1141      // Call recvmsg with MSG_PEEK and MSG_TRUNC. This will peek at the message
  1142      // and return it's full length.
  1143      // See: MSG_TRUNC http://man7.org/linux/man-pages/man2/recv.2.html
  1144      ASSERT_THAT(
  1145          peeklen = RetryEINTR(recvmsg)(fd.get(), &msg, MSG_PEEK | MSG_TRUNC),
  1146          SyscallSucceeds());
  1147  
  1148      // Message should always be truncated.
  1149      ASSERT_EQ(msg.msg_flags & MSG_TRUNC, MSG_TRUNC);
  1150      ASSERT_NE(peeklen, 0);
  1151  
  1152      // Reset the message flags for the next call.
  1153      msg.msg_flags = 0;
  1154  
  1155      // Make the actual call to recvmsg to get the actual data. We will use
  1156      // the length returned from the peek call for the allocated buffer size..
  1157      std::vector<char> buf(peeklen);
  1158      iov.iov_base = buf.data();
  1159      iov.iov_len = buf.size();
  1160      ASSERT_THAT(len = RetryEINTR(recvmsg)(fd.get(), &msg, 0),
  1161                  SyscallSucceeds());
  1162  
  1163      // Message should not be truncated since we allocated the correct buffer
  1164      // size.
  1165      EXPECT_NE(msg.msg_flags & MSG_TRUNC, MSG_TRUNC);
  1166  
  1167      // MSG_PEEK should have left data on the socket and the subsequent call
  1168      // with should have retrieved the same data. Both calls should have
  1169      // returned the message's full length so they should be equal.
  1170      ASSERT_NE(len, 0);
  1171      ASSERT_EQ(peeklen, len);
  1172  
  1173      for (struct nlmsghdr* hdr = reinterpret_cast<struct nlmsghdr*>(buf.data());
  1174           NLMSG_OK(hdr, len); hdr = NLMSG_NEXT(hdr, len)) {
  1175        type = hdr->nlmsg_type;
  1176      }
  1177    } while (type != NLMSG_DONE && type != NLMSG_ERROR);
  1178  }
  1179  
  1180  // No SCM_CREDENTIALS are received without SO_PASSCRED set.
  1181  TEST(NetlinkRouteTest, NoPasscredNoCreds) {
  1182    FileDescriptor fd =
  1183        ASSERT_NO_ERRNO_AND_VALUE(NetlinkBoundSocket(NETLINK_ROUTE));
  1184  
  1185    ASSERT_THAT(setsockopt(fd.get(), SOL_SOCKET, SO_PASSCRED, &kSockOptOff,
  1186                           sizeof(kSockOptOff)),
  1187                SyscallSucceeds());
  1188  
  1189    struct request {
  1190      struct nlmsghdr hdr;
  1191      struct rtgenmsg rgm;
  1192    };
  1193  
  1194    struct request req;
  1195    req.hdr.nlmsg_len = sizeof(req);
  1196    req.hdr.nlmsg_type = RTM_GETADDR;
  1197    req.hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
  1198    req.hdr.nlmsg_seq = kSeq;
  1199    req.rgm.rtgen_family = AF_UNSPEC;
  1200  
  1201    struct iovec iov = {};
  1202    iov.iov_base = &req;
  1203    iov.iov_len = sizeof(req);
  1204  
  1205    struct msghdr msg = {};
  1206    msg.msg_iov = &iov;
  1207    msg.msg_iovlen = 1;
  1208  
  1209    ASSERT_THAT(RetryEINTR(sendmsg)(fd.get(), &msg, 0), SyscallSucceeds());
  1210  
  1211    iov.iov_base = NULL;
  1212    iov.iov_len = 0;
  1213  
  1214    char control[CMSG_SPACE(sizeof(struct ucred))] = {};
  1215    msg.msg_control = control;
  1216    msg.msg_controllen = sizeof(control);
  1217  
  1218    // Note: This test assumes at least one message is returned by the
  1219    // RTM_GETADDR request.
  1220    ASSERT_THAT(RetryEINTR(recvmsg)(fd.get(), &msg, 0), SyscallSucceeds());
  1221  
  1222    // No control messages.
  1223    EXPECT_EQ(CMSG_FIRSTHDR(&msg), nullptr);
  1224  }
  1225  
  1226  // SCM_CREDENTIALS are received with SO_PASSCRED set.
  1227  TEST(NetlinkRouteTest, PasscredCreds) {
  1228    FileDescriptor fd =
  1229        ASSERT_NO_ERRNO_AND_VALUE(NetlinkBoundSocket(NETLINK_ROUTE));
  1230  
  1231    ASSERT_THAT(setsockopt(fd.get(), SOL_SOCKET, SO_PASSCRED, &kSockOptOn,
  1232                           sizeof(kSockOptOn)),
  1233                SyscallSucceeds());
  1234  
  1235    struct request {
  1236      struct nlmsghdr hdr;
  1237      struct rtgenmsg rgm;
  1238    };
  1239  
  1240    struct request req;
  1241    req.hdr.nlmsg_len = sizeof(req);
  1242    req.hdr.nlmsg_type = RTM_GETADDR;
  1243    req.hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
  1244    req.hdr.nlmsg_seq = kSeq;
  1245    req.rgm.rtgen_family = AF_UNSPEC;
  1246  
  1247    struct iovec iov = {};
  1248    iov.iov_base = &req;
  1249    iov.iov_len = sizeof(req);
  1250  
  1251    struct msghdr msg = {};
  1252    msg.msg_iov = &iov;
  1253    msg.msg_iovlen = 1;
  1254  
  1255    ASSERT_THAT(RetryEINTR(sendmsg)(fd.get(), &msg, 0), SyscallSucceeds());
  1256  
  1257    iov.iov_base = NULL;
  1258    iov.iov_len = 0;
  1259  
  1260    char control[CMSG_SPACE(sizeof(struct ucred))] = {};
  1261    msg.msg_control = control;
  1262    msg.msg_controllen = sizeof(control);
  1263  
  1264    // Note: This test assumes at least one message is returned by the
  1265    // RTM_GETADDR request.
  1266    ASSERT_THAT(RetryEINTR(recvmsg)(fd.get(), &msg, 0), SyscallSucceeds());
  1267  
  1268    struct ucred creds;
  1269    struct cmsghdr* cmsg = CMSG_FIRSTHDR(&msg);
  1270    ASSERT_NE(cmsg, nullptr);
  1271    ASSERT_EQ(cmsg->cmsg_len, CMSG_LEN(sizeof(creds)));
  1272    ASSERT_EQ(cmsg->cmsg_level, SOL_SOCKET);
  1273    ASSERT_EQ(cmsg->cmsg_type, SCM_CREDENTIALS);
  1274  
  1275    memcpy(&creds, CMSG_DATA(cmsg), sizeof(creds));
  1276  
  1277    // The peer is the kernel, which is "PID" 0.
  1278    EXPECT_EQ(creds.pid, 0);
  1279    // The kernel identifies as root. Also allow nobody in case this test is
  1280    // running in a userns without root mapped.
  1281    EXPECT_THAT(creds.uid, AnyOf(Eq(0), Eq(65534)));
  1282    EXPECT_THAT(creds.gid, AnyOf(Eq(0), Eq(65534)));
  1283  }
  1284  
  1285  }  // namespace
  1286  
  1287  }  // namespace testing
  1288  }  // namespace gvisor