From 32537556ff22fbc4d2f0dcfc260399e74af5ba22 Mon Sep 17 00:00:00 2001 From: Jeff Martin Date: Mon, 7 Aug 2023 15:33:53 -0700 Subject: [PATCH] Add test coverage for RTM_NEWROUTE & RTM_DELROUTE PiperOrigin-RevId: 554612706 --- test/syscalls/linux/BUILD | 4 + test/syscalls/linux/socket_netlink_route.cc | 64 ++++++++- .../linux/socket_netlink_route_util.cc | 123 +++++++++++++++--- .../linux/socket_netlink_route_util.h | 8 ++ 4 files changed, 183 insertions(+), 16 deletions(-) diff --git a/test/syscalls/linux/BUILD b/test/syscalls/linux/BUILD index a2fcc8059..ef71995ea 100644 --- a/test/syscalls/linux/BUILD +++ b/test/syscalls/linux/BUILD @@ -151,6 +151,8 @@ cc_library( hdrs = ["socket_netlink_route_util.h"], deps = [ ":socket_netlink_util", + "//test/util:file_descriptor", + "//test/util:posix_error", ], ) @@ -3371,6 +3373,8 @@ cc_binary( "//test/util:capability_util", "//test/util:cleanup", "//test/util:file_descriptor", + "//test/util:posix_error", + "//test/util:save_util", "//test/util:socket_util", "@com_google_absl//absl/strings:str_format", gtest, diff --git a/test/syscalls/linux/socket_netlink_route.cc b/test/syscalls/linux/socket_netlink_route.cc index 48dc525f5..2239e714b 100644 --- a/test/syscalls/linux/socket_netlink_route.cc +++ b/test/syscalls/linux/socket_netlink_route.cc @@ -22,16 +22,21 @@ #include #include +#include #include +#include #include +#include "gmock/gmock.h" #include "gtest/gtest.h" #include "absl/strings/str_format.h" #include "test/syscalls/linux/socket_netlink_route_util.h" #include "test/syscalls/linux/socket_netlink_util.h" -#include "test/util/capability_util.h" #include "test/util/cleanup.h" #include "test/util/file_descriptor.h" +#include "test/util/linux_capability_util.h" +#include "test/util/posix_error.h" +#include "test/util/save_util.h" #include "test/util/socket_util.h" #include "test/util/test_util.h" @@ -823,6 +828,63 @@ TEST(NetlinkRouteTest, GetRouteRequest) { EXPECT_TRUE(rtDstFound); } +// NetlinkRouteTest with a single parameter that must be AF_INET or AF_INET6. +using NetlinkRouteIpInvariantTest = ::testing::TestWithParam; + +TEST_P(NetlinkRouteIpInvariantTest, AddAndRemoveRoute) { + // Gvisor does not support `RTM_NEWROUTE` or `RTM_DELROUTE`. + SKIP_IF(IsRunningOnGvisor()); + // CAP_NET_ADMIN is required to modify the routing table. + SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_ADMIN))); + + // Based on the test parameter, build an IPv4 or IPv6 destination subnet. + int family = GetParam(); + struct in_addr dst_v4; + struct in6_addr dst_v6; + void* dst = nullptr; + int dst_len; + int prefixlen; + switch (family) { + case AF_INET: + ASSERT_EQ(inet_pton(family, "192.0.2.0", &dst_v4), 1); + prefixlen = 24; + dst = &dst_v4; + dst_len = sizeof(dst_v4); + break; + case AF_INET6: + ASSERT_EQ(inet_pton(family, "2001:db8::", &dst_v6), 1); + prefixlen = 64; + dst = &dst_v6; + dst_len = sizeof(dst_v6); + break; + default: + FAIL() << "address family must be AF_INET or AF_INET6"; + } + + Link loopback_link = ASSERT_NO_ERRNO_AND_VALUE(LoopbackLink()); + + // Create should succeed, as no such route in kernel. + ASSERT_NO_ERRNO( + AddUnicastRoute(loopback_link.index, family, prefixlen, dst, dst_len)); + + // Second create should fail, as we already created the route above. + EXPECT_THAT( + AddUnicastRoute(loopback_link.index, family, prefixlen, dst, dst_len), + PosixErrorIs(EEXIST, _)); + + // First delete should succeed, as route exists. + EXPECT_NO_ERRNO( + DelUnicastRoute(loopback_link.index, family, prefixlen, dst, dst_len)); + + // Second delete should fail, as route no longer exists. + EXPECT_THAT( + DelUnicastRoute(loopback_link.index, family, prefixlen, dst, dst_len), + PosixErrorIs(ESRCH, _)); +} + +INSTANTIATE_TEST_SUITE_P(AddAndRemoveRoute, NetlinkRouteIpInvariantTest, + ::testing::Values(AF_INET, AF_INET6)); + // RecvmsgTrunc tests the recvmsg MSG_TRUNC flag with zero length output // buffer. MSG_TRUNC with a zero length buffer should consume subsequent // messages off the socket. diff --git a/test/syscalls/linux/socket_netlink_route_util.cc b/test/syscalls/linux/socket_netlink_route_util.cc index 46f749c7c..11f0c27c2 100644 --- a/test/syscalls/linux/socket_netlink_route_util.cc +++ b/test/syscalls/linux/socket_netlink_route_util.cc @@ -18,39 +18,46 @@ #include #include +#include +#include +#include + #include "test/syscalls/linux/socket_netlink_util.h" +#include "test/util/file_descriptor.h" +#include "test/util/posix_error.h" namespace gvisor { namespace testing { namespace { constexpr uint32_t kSeq = 12345; +constexpr uint32_t kMetric = 999; -// Types of address modifications that may be performed on an interface. -enum class LinkAddrModification { +// Types of modifications that may be performed to a Netlink resource. +enum class NetlinkModification { kAdd, kAddExclusive, kReplace, kDelete, }; -// Populates |hdr| with appripriate values for the modification type. -PosixError PopulateNlmsghdr(LinkAddrModification modification, - struct nlmsghdr* hdr) { +// Populates |hdr| with appropriate values for the modification type. +PosixError PopulateLinkAddrNlmsghdr(NetlinkModification modification, + struct nlmsghdr* hdr) { switch (modification) { - case LinkAddrModification::kAdd: + case NetlinkModification::kAdd: hdr->nlmsg_type = RTM_NEWADDR; hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; return NoError(); - case LinkAddrModification::kAddExclusive: + case NetlinkModification::kAddExclusive: hdr->nlmsg_type = RTM_NEWADDR; hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_EXCL | NLM_F_ACK; return NoError(); - case LinkAddrModification::kReplace: + case NetlinkModification::kReplace: hdr->nlmsg_type = RTM_NEWADDR; hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_REPLACE | NLM_F_ACK; return NoError(); - case LinkAddrModification::kDelete: + case NetlinkModification::kDelete: hdr->nlmsg_type = RTM_DELADDR; hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; return NoError(); @@ -59,10 +66,35 @@ PosixError PopulateNlmsghdr(LinkAddrModification modification, return PosixError(EINVAL); } +// Populates |hdr| with appropriate values for the modification type. +PosixError PopulateRouteNlmsghdr(NetlinkModification modification, + struct nlmsghdr* hdr) { + switch (modification) { + case NetlinkModification::kAdd: + hdr->nlmsg_type = RTM_NEWROUTE; + hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK | NLM_F_CREATE; + return NoError(); + case NetlinkModification::kAddExclusive: + hdr->nlmsg_type = RTM_NEWROUTE; + hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_EXCL | NLM_F_ACK; + return NoError(); + case NetlinkModification::kReplace: + hdr->nlmsg_type = RTM_NEWROUTE; + hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_REPLACE | NLM_F_ACK; + return NoError(); + case NetlinkModification::kDelete: + hdr->nlmsg_type = RTM_DELROUTE; + hdr->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; + return NoError(); + } + + return PosixError(EINVAL); +} + // Adds or removes the specified address from the specified interface. PosixError LinkModifyLocalAddr(int index, int family, int prefixlen, const void* addr, int addrlen, - LinkAddrModification modification) { + NetlinkModification modification) { ASSIGN_OR_RETURN_ERRNO(FileDescriptor fd, NetlinkBoundSocket(NETLINK_ROUTE)); struct request { @@ -72,7 +104,7 @@ PosixError LinkModifyLocalAddr(int index, int family, int prefixlen, }; struct request req = {}; - PosixError err = PopulateNlmsghdr(modification, &req.hdr); + PosixError err = PopulateLinkAddrNlmsghdr(modification, &req.hdr); if (!err.ok()) { return err; } @@ -92,6 +124,55 @@ PosixError LinkModifyLocalAddr(int index, int family, int prefixlen, return NetlinkRequestAckOrError(fd, kSeq, &req, req.hdr.nlmsg_len); } +// Adds or removes the specified route. +PosixError ModifyUnicastRoute(int interface, int family, int prefixlen, + const void* dst, int dstlen, + NetlinkModification modification) { + ASSIGN_OR_RETURN_ERRNO(FileDescriptor fd, NetlinkBoundSocket(NETLINK_ROUTE)); + + struct request { + struct nlmsghdr hdr; + struct rtmsg route; + char attrbuf[512]; + }; + + struct request req = {}; + PosixError err = PopulateRouteNlmsghdr(modification, &req.hdr); + if (!err.ok()) { + return err; + } + req.hdr.nlmsg_len = NLMSG_LENGTH(sizeof(req.route)); + req.hdr.nlmsg_seq = kSeq; + req.route.rtm_dst_len = prefixlen; + req.route.rtm_family = family; + req.route.rtm_type = RTN_UNICAST; + + struct rtattr* rta_oif = reinterpret_cast( + reinterpret_cast(&req) + NLMSG_ALIGN(req.hdr.nlmsg_len)); + rta_oif->rta_type = RTA_OIF; + rta_oif->rta_len = RTA_LENGTH(sizeof(interface)); + req.hdr.nlmsg_len = + NLMSG_ALIGN(req.hdr.nlmsg_len) + RTA_LENGTH(sizeof(interface)); + memcpy(RTA_DATA(rta_oif), &interface, sizeof(interface)); + + struct rtattr* rta_priority = reinterpret_cast( + reinterpret_cast(&req) + NLMSG_ALIGN(req.hdr.nlmsg_len)); + rta_priority->rta_type = RTA_PRIORITY; + rta_priority->rta_len = RTA_LENGTH(sizeof(kMetric)); + req.hdr.nlmsg_len = + NLMSG_ALIGN(req.hdr.nlmsg_len) + RTA_LENGTH(sizeof(kMetric)); + memcpy(RTA_DATA(rta_priority), &kMetric, sizeof(kMetric)); + + struct rtattr* rta_dst = reinterpret_cast( + reinterpret_cast(&req) + NLMSG_ALIGN(req.hdr.nlmsg_len)); + rta_dst->rta_type = RTA_DST; + rta_dst->rta_len = RTA_LENGTH(dstlen); + req.hdr.nlmsg_len = NLMSG_ALIGN(req.hdr.nlmsg_len) + RTA_LENGTH(dstlen); + memcpy(RTA_DATA(rta_dst), dst, dstlen); + + return NetlinkRequestAckOrError(fd, kSeq, &req, req.hdr.nlmsg_len); +} + } // namespace PosixError DumpLinks( @@ -151,25 +232,25 @@ PosixErrorOr LoopbackLink() { PosixError LinkAddLocalAddr(int index, int family, int prefixlen, const void* addr, int addrlen) { return LinkModifyLocalAddr(index, family, prefixlen, addr, addrlen, - LinkAddrModification::kAdd); + NetlinkModification::kAdd); } PosixError LinkAddExclusiveLocalAddr(int index, int family, int prefixlen, const void* addr, int addrlen) { return LinkModifyLocalAddr(index, family, prefixlen, addr, addrlen, - LinkAddrModification::kAddExclusive); + NetlinkModification::kAddExclusive); } PosixError LinkReplaceLocalAddr(int index, int family, int prefixlen, const void* addr, int addrlen) { return LinkModifyLocalAddr(index, family, prefixlen, addr, addrlen, - LinkAddrModification::kReplace); + NetlinkModification::kReplace); } PosixError LinkDelLocalAddr(int index, int family, int prefixlen, const void* addr, int addrlen) { return LinkModifyLocalAddr(index, family, prefixlen, addr, addrlen, - LinkAddrModification::kDelete); + NetlinkModification::kDelete); } PosixError LinkChangeFlags(int index, unsigned int flags, unsigned int change) { @@ -219,5 +300,17 @@ PosixError LinkSetMacAddr(int index, const void* addr, int addrlen) { return NetlinkRequestAckOrError(fd, kSeq, &req, req.hdr.nlmsg_len); } +PosixError AddUnicastRoute(int interface, int family, int prefixlen, + const void* dst, int dstlen) { + return ModifyUnicastRoute(interface, family, prefixlen, dst, dstlen, + NetlinkModification::kAdd); +} + +PosixError DelUnicastRoute(int interface, int family, int prefixlen, + const void* dst, int dstlen) { + return ModifyUnicastRoute(interface, family, prefixlen, dst, dstlen, + NetlinkModification::kDelete); +} + } // namespace testing } // namespace gvisor diff --git a/test/syscalls/linux/socket_netlink_route_util.h b/test/syscalls/linux/socket_netlink_route_util.h index eaa91ad79..500da85bf 100644 --- a/test/syscalls/linux/socket_netlink_route_util.h +++ b/test/syscalls/linux/socket_netlink_route_util.h @@ -62,6 +62,14 @@ PosixError LinkChangeFlags(int index, unsigned int flags, unsigned int change); // LinkSetMacAddr sets IFLA_ADDRESS attribute of the interface. PosixError LinkSetMacAddr(int index, const void* addr, int addrlen); +// AddRoute adds a route to the given dst subnet via the given interface. +PosixError AddUnicastRoute(int interface, int family, int prefixlen, + const void* dst, int dstlen); + +// DelRoute removes a route to the given dst subnet via the given interface. +PosixError DelUnicastRoute(int interface, int family, int prefixlen, + const void* dst, int dstlen); + } // namespace testing } // namespace gvisor