Add test coverage for RTM_NEWROUTE & RTM_DELROUTE

PiperOrigin-RevId: 554612706
This commit is contained in:
Jeff Martin
2023-08-07 15:37:13 -07:00
committed by gVisor bot
parent 4c5d0ffc37
commit 32537556ff
4 changed files with 183 additions and 16 deletions
+4
View File
@@ -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,
+63 -1
View File
@@ -22,16 +22,21 @@
#include <sys/types.h>
#include <unistd.h>
#include <cerrno>
#include <iostream>
#include <utility>
#include <vector>
#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<int>;
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.
+108 -15
View File
@@ -18,39 +18,46 @@
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
#include <cerrno>
#include <cstdint>
#include <cstring>
#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<struct rtattr*>(
reinterpret_cast<int8_t*>(&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<struct rtattr*>(
reinterpret_cast<int8_t*>(&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<struct rtattr*>(
reinterpret_cast<int8_t*>(&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<Link> 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
@@ -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