182 lines
5.8 KiB
C++
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
|