154 lines
4.9 KiB
C++
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
|