gvisor/test/syscalls/linux/socket_netdevice.cc

182 lines
5.8 KiB
C++

// Copyright 2018 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <linux/netlink.h>
#include <linux/rtnetlink.h>
#include <linux/sockios.h>
#include <sys/ioctl.h>
#include <sys/socket.h>
#include "gtest/gtest.h"
#include "absl/base/internal/endian.h"
#include "test/syscalls/linux/socket_netlink_util.h"
#include "test/syscalls/linux/socket_test_util.h"
#include "test/util/file_descriptor.h"
#include "test/util/test_util.h"
// Tests for netdevice queries.
namespace gvisor {
namespace testing {
namespace {
using ::testing::AnyOf;
using ::testing::Eq;
TEST(NetdeviceTest, Loopback) {
FileDescriptor sock =
ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_INET, SOCK_DGRAM, 0));
// Prepare the request.
struct ifreq ifr;
snprintf(ifr.ifr_name, IFNAMSIZ, "lo");
// Check for a non-zero interface index.
ASSERT_THAT(ioctl(sock.get(), SIOCGIFINDEX, &ifr), SyscallSucceeds());
EXPECT_NE(ifr.ifr_ifindex, 0);
// Check that the loopback is zero hardware address.
ASSERT_THAT(ioctl(sock.get(), SIOCGIFHWADDR, &ifr), SyscallSucceeds());
EXPECT_EQ(ifr.ifr_hwaddr.sa_data[0], 0);
EXPECT_EQ(ifr.ifr_hwaddr.sa_data[1], 0);
EXPECT_EQ(ifr.ifr_hwaddr.sa_data[2], 0);
EXPECT_EQ(ifr.ifr_hwaddr.sa_data[3], 0);
EXPECT_EQ(ifr.ifr_hwaddr.sa_data[4], 0);
EXPECT_EQ(ifr.ifr_hwaddr.sa_data[5], 0);
}
TEST(NetdeviceTest, Netmask) {
// We need an interface index to identify the loopback device.
FileDescriptor sock =
ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_INET, SOCK_DGRAM, 0));
struct ifreq ifr;
snprintf(ifr.ifr_name, IFNAMSIZ, "lo");
ASSERT_THAT(ioctl(sock.get(), SIOCGIFINDEX, &ifr), SyscallSucceeds());
EXPECT_NE(ifr.ifr_ifindex, 0);
// Use a netlink socket to get the netmask, which we'll then compare to the
// netmask obtained via ioctl.
FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(NetlinkBoundSocket());
uint32_t port = ASSERT_NO_ERRNO_AND_VALUE(NetlinkPortID(fd.get()));
struct request {
struct nlmsghdr hdr;
struct rtgenmsg rgm;
};
constexpr uint32_t kSeq = 12345;
struct request req;
req.hdr.nlmsg_len = sizeof(req);
req.hdr.nlmsg_type = RTM_GETADDR;
req.hdr.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP;
req.hdr.nlmsg_seq = kSeq;
req.rgm.rtgen_family = AF_UNSPEC;
// Iterate through messages until we find the one containing the prefix length
// (i.e. netmask) for the loopback device.
int prefixlen = -1;
ASSERT_NO_ERRNO(NetlinkRequestResponse(
fd, &req, sizeof(req), [&](const struct nlmsghdr *hdr) {
EXPECT_THAT(hdr->nlmsg_type, AnyOf(Eq(RTM_NEWADDR), Eq(NLMSG_DONE)));
EXPECT_TRUE((hdr->nlmsg_flags & NLM_F_MULTI) == NLM_F_MULTI)
<< std::hex << hdr->nlmsg_flags;
EXPECT_EQ(hdr->nlmsg_seq, kSeq);
EXPECT_EQ(hdr->nlmsg_pid, port);
if (hdr->nlmsg_type != RTM_NEWADDR) {
return;
}
// RTM_NEWADDR contains at least the header and ifaddrmsg.
EXPECT_GE(hdr->nlmsg_len, sizeof(*hdr) + sizeof(struct ifaddrmsg));
struct ifaddrmsg *ifaddrmsg =
reinterpret_cast<struct ifaddrmsg *>(NLMSG_DATA(hdr));
if (ifaddrmsg->ifa_index == static_cast<uint32_t>(ifr.ifr_ifindex) &&
ifaddrmsg->ifa_family == AF_INET) {
prefixlen = ifaddrmsg->ifa_prefixlen;
}
}));
ASSERT_GE(prefixlen, 0);
// Netmask is stored big endian in struct sockaddr_in, so we do the same for
// comparison.
uint32_t mask = 0xffffffff << (32 - prefixlen);
mask = absl::gbswap_32(mask);
// Check that the loopback interface has the correct subnet mask.
snprintf(ifr.ifr_name, IFNAMSIZ, "lo");
ASSERT_THAT(ioctl(sock.get(), SIOCGIFNETMASK, &ifr), SyscallSucceeds());
EXPECT_EQ(ifr.ifr_netmask.sa_family, AF_INET);
struct sockaddr_in *sin =
reinterpret_cast<struct sockaddr_in *>(&ifr.ifr_netmask);
EXPECT_EQ(sin->sin_addr.s_addr, mask);
}
TEST(NetdeviceTest, InterfaceName) {
FileDescriptor sock =
ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_INET, SOCK_DGRAM, 0));
// Prepare the request.
struct ifreq ifr;
snprintf(ifr.ifr_name, IFNAMSIZ, "lo");
// Check for a non-zero interface index.
ASSERT_THAT(ioctl(sock.get(), SIOCGIFINDEX, &ifr), SyscallSucceeds());
EXPECT_NE(ifr.ifr_ifindex, 0);
// Check that SIOCGIFNAME finds the loopback interface.
snprintf(ifr.ifr_name, IFNAMSIZ, "foo");
ASSERT_THAT(ioctl(sock.get(), SIOCGIFNAME, &ifr), SyscallSucceeds());
EXPECT_STREQ(ifr.ifr_name, "lo");
}
TEST(NetdeviceTest, InterfaceFlags) {
FileDescriptor sock =
ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_INET, SOCK_DGRAM, 0));
// Prepare the request.
struct ifreq ifr;
snprintf(ifr.ifr_name, IFNAMSIZ, "lo");
// Check that SIOCGIFFLAGS marks the interface with IFF_LOOPBACK, IFF_UP, and
// IFF_RUNNING.
ASSERT_THAT(ioctl(sock.get(), SIOCGIFFLAGS, &ifr), SyscallSucceeds());
EXPECT_EQ(ifr.ifr_flags & IFF_UP, IFF_UP);
EXPECT_EQ(ifr.ifr_flags & IFF_RUNNING, IFF_RUNNING);
}
TEST(NetdeviceTest, InterfaceMTU) {
FileDescriptor sock =
ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_INET, SOCK_DGRAM, 0));
// Prepare the request.
struct ifreq ifr = {};
snprintf(ifr.ifr_name, IFNAMSIZ, "lo");
// Check that SIOCGIFMTU returns a nonzero MTU.
ASSERT_THAT(ioctl(sock.get(), SIOCGIFMTU, &ifr), SyscallSucceeds());
EXPECT_GT(ifr.ifr_mtu, 0);
}
} // namespace
} // namespace testing
} // namespace gvisor