gvisor/test/syscalls/linux/socket_netlink.cc

154 lines
4.9 KiB
C++

// Copyright 2018 The gVisor Authors.
//
// 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 <sys/socket.h>
#include <sys/types.h>
#include <unistd.h>
#include "gtest/gtest.h"
#include "test/syscalls/linux/socket_test_util.h"
#include "test/util/file_descriptor.h"
#include "test/util/test_util.h"
// Tests for all netlink socket protocols.
namespace gvisor {
namespace testing {
namespace {
// NetlinkTest parameter is the protocol to test.
using NetlinkTest = ::testing::TestWithParam<int>;
// Netlink sockets must be SOCK_DGRAM or SOCK_RAW.
TEST_P(NetlinkTest, Types) {
const int protocol = GetParam();
EXPECT_THAT(socket(AF_NETLINK, SOCK_STREAM, protocol),
SyscallFailsWithErrno(ESOCKTNOSUPPORT));
EXPECT_THAT(socket(AF_NETLINK, SOCK_SEQPACKET, protocol),
SyscallFailsWithErrno(ESOCKTNOSUPPORT));
EXPECT_THAT(socket(AF_NETLINK, SOCK_RDM, protocol),
SyscallFailsWithErrno(ESOCKTNOSUPPORT));
EXPECT_THAT(socket(AF_NETLINK, SOCK_DCCP, protocol),
SyscallFailsWithErrno(ESOCKTNOSUPPORT));
EXPECT_THAT(socket(AF_NETLINK, SOCK_PACKET, protocol),
SyscallFailsWithErrno(ESOCKTNOSUPPORT));
int fd;
EXPECT_THAT(fd = socket(AF_NETLINK, SOCK_DGRAM, protocol), SyscallSucceeds());
EXPECT_THAT(close(fd), SyscallSucceeds());
EXPECT_THAT(fd = socket(AF_NETLINK, SOCK_RAW, protocol), SyscallSucceeds());
EXPECT_THAT(close(fd), SyscallSucceeds());
}
TEST_P(NetlinkTest, AutomaticPort) {
const int protocol = GetParam();
FileDescriptor fd =
ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_NETLINK, SOCK_RAW, protocol));
struct sockaddr_nl addr = {};
addr.nl_family = AF_NETLINK;
EXPECT_THAT(
bind(fd.get(), reinterpret_cast<struct sockaddr*>(&addr), sizeof(addr)),
SyscallSucceeds());
socklen_t addrlen = sizeof(addr);
EXPECT_THAT(getsockname(fd.get(), reinterpret_cast<struct sockaddr*>(&addr),
&addrlen),
SyscallSucceeds());
EXPECT_EQ(addrlen, sizeof(addr));
// This is the only netlink socket in the process, so it should get the PID as
// the port id.
//
// N.B. Another process could theoretically have explicitly reserved our pid
// as a port ID, but that is very unlikely.
EXPECT_EQ(addr.nl_pid, getpid());
}
// Calling connect automatically binds to an automatic port.
TEST_P(NetlinkTest, ConnectBinds) {
const int protocol = GetParam();
FileDescriptor fd =
ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_NETLINK, SOCK_RAW, protocol));
struct sockaddr_nl addr = {};
addr.nl_family = AF_NETLINK;
EXPECT_THAT(connect(fd.get(), reinterpret_cast<struct sockaddr*>(&addr),
sizeof(addr)),
SyscallSucceeds());
socklen_t addrlen = sizeof(addr);
EXPECT_THAT(getsockname(fd.get(), reinterpret_cast<struct sockaddr*>(&addr),
&addrlen),
SyscallSucceeds());
EXPECT_EQ(addrlen, sizeof(addr));
// Each test is running in a pid namespace, so another process can explicitly
// reserve our pid as a port ID. In this case, a negative portid value will be
// set.
if (static_cast<pid_t>(addr.nl_pid) > 0) {
EXPECT_EQ(addr.nl_pid, getpid());
}
memset(&addr, 0, sizeof(addr));
addr.nl_family = AF_NETLINK;
// Connecting again is allowed, but keeps the same port.
EXPECT_THAT(connect(fd.get(), reinterpret_cast<struct sockaddr*>(&addr),
sizeof(addr)),
SyscallSucceeds());
addrlen = sizeof(addr);
EXPECT_THAT(getsockname(fd.get(), reinterpret_cast<struct sockaddr*>(&addr),
&addrlen),
SyscallSucceeds());
EXPECT_EQ(addrlen, sizeof(addr));
EXPECT_EQ(addr.nl_pid, getpid());
}
TEST_P(NetlinkTest, GetPeerName) {
const int protocol = GetParam();
FileDescriptor fd =
ASSERT_NO_ERRNO_AND_VALUE(Socket(AF_NETLINK, SOCK_RAW, protocol));
struct sockaddr_nl addr = {};
socklen_t addrlen = sizeof(addr);
EXPECT_THAT(getpeername(fd.get(), reinterpret_cast<struct sockaddr*>(&addr),
&addrlen),
SyscallSucceeds());
EXPECT_EQ(addrlen, sizeof(addr));
EXPECT_EQ(addr.nl_family, AF_NETLINK);
// Peer is the kernel if we didn't connect elsewhere.
EXPECT_EQ(addr.nl_pid, 0);
}
INSTANTIATE_TEST_SUITE_P(ProtocolTest, NetlinkTest,
::testing::Values(NETLINK_ROUTE,
NETLINK_KOBJECT_UEVENT));
} // namespace
} // namespace testing
} // namespace gvisor