gvisor/test/syscalls/linux/socket_ipv4_udp_unbound_net...

203 lines
8.4 KiB
C++

// Copyright 2020 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 "test/syscalls/linux/socket_ipv4_udp_unbound_netlink.h"
#include <arpa/inet.h>
#include <poll.h>
#include "gtest/gtest.h"
#include "test/syscalls/linux/socket_netlink_route_util.h"
#include "test/util/capability_util.h"
#include "test/util/cleanup.h"
namespace gvisor {
namespace testing {
constexpr size_t kSendBufSize = 200;
// Checks that the loopback interface considers itself bound to all IPs in an
// associated subnet.
TEST_P(IPv4UDPUnboundSocketNetlinkTest, JoinSubnet) {
SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_ADMIN)));
// Add an IP address to the loopback interface.
Link loopback_link = ASSERT_NO_ERRNO_AND_VALUE(LoopbackLink());
struct in_addr addr;
ASSERT_EQ(1, inet_pton(AF_INET, "192.0.2.1", &addr));
ASSERT_NO_ERRNO(LinkAddLocalAddr(loopback_link.index, AF_INET,
/*prefixlen=*/24, &addr, sizeof(addr)));
Cleanup defer_addr_removal = Cleanup(
[loopback_link = std::move(loopback_link), addr = std::move(addr)] {
EXPECT_NO_ERRNO(LinkDelLocalAddr(loopback_link.index, AF_INET,
/*prefixlen=*/24, &addr,
sizeof(addr)));
});
auto snd_sock = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
auto rcv_sock = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
// Send from an unassigned address but an address that is in the subnet
// associated with the loopback interface.
TestAddress sender_addr("V4NotAssignd1");
sender_addr.addr.ss_family = AF_INET;
sender_addr.addr_len = sizeof(sockaddr_in);
ASSERT_EQ(1, inet_pton(AF_INET, "192.0.2.2",
&(reinterpret_cast<sockaddr_in*>(&sender_addr.addr)
->sin_addr.s_addr)));
ASSERT_THAT(bind(snd_sock->get(), AsSockAddr(&sender_addr.addr),
sender_addr.addr_len),
SyscallSucceeds());
// Send the packet to an unassigned address but an address that is in the
// subnet associated with the loopback interface.
TestAddress receiver_addr("V4NotAssigned2");
receiver_addr.addr.ss_family = AF_INET;
receiver_addr.addr_len = sizeof(sockaddr_in);
ASSERT_EQ(1, inet_pton(AF_INET, "192.0.2.254",
&(reinterpret_cast<sockaddr_in*>(&receiver_addr.addr)
->sin_addr.s_addr)));
ASSERT_THAT(bind(rcv_sock->get(), AsSockAddr(&receiver_addr.addr),
receiver_addr.addr_len),
SyscallSucceeds());
socklen_t receiver_addr_len = receiver_addr.addr_len;
ASSERT_THAT(getsockname(rcv_sock->get(), AsSockAddr(&receiver_addr.addr),
&receiver_addr_len),
SyscallSucceeds());
ASSERT_EQ(receiver_addr_len, receiver_addr.addr_len);
char send_buf[kSendBufSize];
RandomizeBuffer(send_buf, kSendBufSize);
ASSERT_THAT(RetryEINTR(sendto)(snd_sock->get(), send_buf, kSendBufSize, 0,
AsSockAddr(&receiver_addr.addr),
receiver_addr.addr_len),
SyscallSucceedsWithValue(kSendBufSize));
// Check that we received the packet.
char recv_buf[kSendBufSize] = {};
ASSERT_THAT(RetryEINTR(recv)(rcv_sock->get(), recv_buf, kSendBufSize, 0),
SyscallSucceedsWithValue(kSendBufSize));
ASSERT_EQ(0, memcmp(send_buf, recv_buf, kSendBufSize));
}
// Tests that broadcast packets are delivered to all interested sockets
// (wildcard and broadcast address specified sockets).
//
// Note, we cannot test the IPv4 Broadcast (255.255.255.255) because we do
// not have a route to it.
TEST_P(IPv4UDPUnboundSocketNetlinkTest, ReuseAddrSubnetDirectedBroadcast) {
constexpr uint16_t kPort = 9876;
// Wait up to 20 seconds for the data.
constexpr int kPollTimeoutMs = 20000;
// Number of sockets per socket type.
constexpr int kNumSocketsPerType = 2;
SKIP_IF(!ASSERT_NO_ERRNO_AND_VALUE(HaveCapability(CAP_NET_ADMIN)));
// Add an IP address to the loopback interface.
Link loopback_link = ASSERT_NO_ERRNO_AND_VALUE(LoopbackLink());
struct in_addr addr;
ASSERT_EQ(1, inet_pton(AF_INET, "192.0.2.1", &addr));
ASSERT_NO_ERRNO(LinkAddLocalAddr(loopback_link.index, AF_INET,
24 /* prefixlen */, &addr, sizeof(addr)));
Cleanup defer_addr_removal = Cleanup(
[loopback_link = std::move(loopback_link), addr = std::move(addr)] {
EXPECT_NO_ERRNO(LinkDelLocalAddr(loopback_link.index, AF_INET,
/*prefixlen=*/24, &addr,
sizeof(addr)));
});
TestAddress broadcast_address("SubnetBroadcastAddress");
broadcast_address.addr.ss_family = AF_INET;
broadcast_address.addr_len = sizeof(sockaddr_in);
auto broadcast_address_in =
reinterpret_cast<sockaddr_in*>(&broadcast_address.addr);
ASSERT_EQ(1, inet_pton(AF_INET, "192.0.2.255",
&broadcast_address_in->sin_addr.s_addr));
broadcast_address_in->sin_port = htons(kPort);
TestAddress any_address = V4Any();
reinterpret_cast<sockaddr_in*>(&any_address.addr)->sin_port = htons(kPort);
// We create sockets bound to both the wildcard address and the broadcast
// address to make sure both of these types of "broadcast interested" sockets
// receive broadcast packets.
std::vector<std::unique_ptr<FileDescriptor>> socks;
for (bool bind_wildcard : {false, true}) {
// Create multiple sockets for each type of "broadcast interested"
// socket so we can test that all sockets receive the broadcast packet.
for (int i = 0; i < kNumSocketsPerType; i++) {
auto sock = ASSERT_NO_ERRNO_AND_VALUE(NewSocket());
auto idx = socks.size();
ASSERT_THAT(setsockopt(sock->get(), SOL_SOCKET, SO_REUSEADDR, &kSockOptOn,
sizeof(kSockOptOn)),
SyscallSucceedsWithValue(0))
<< "socks[" << idx << "]";
ASSERT_THAT(setsockopt(sock->get(), SOL_SOCKET, SO_BROADCAST, &kSockOptOn,
sizeof(kSockOptOn)),
SyscallSucceedsWithValue(0))
<< "socks[" << idx << "]";
if (bind_wildcard) {
ASSERT_THAT(bind(sock->get(), AsSockAddr(&any_address.addr),
any_address.addr_len),
SyscallSucceeds())
<< "socks[" << idx << "]";
} else {
ASSERT_THAT(bind(sock->get(), AsSockAddr(&broadcast_address.addr),
broadcast_address.addr_len),
SyscallSucceeds())
<< "socks[" << idx << "]";
}
socks.push_back(std::move(sock));
}
}
char send_buf[kSendBufSize];
RandomizeBuffer(send_buf, kSendBufSize);
// Broadcasts from each socket should be received by every socket (including
// the sending socket).
for (long unsigned int w = 0; w < socks.size(); w++) {
auto& w_sock = socks[w];
ASSERT_THAT(RetryEINTR(sendto)(w_sock->get(), send_buf, kSendBufSize, 0,
AsSockAddr(&broadcast_address.addr),
broadcast_address.addr_len),
SyscallSucceedsWithValue(kSendBufSize))
<< "write socks[" << w << "]";
// Check that we received the packet on all sockets.
for (long unsigned int r = 0; r < socks.size(); r++) {
auto& r_sock = socks[r];
struct pollfd poll_fd = {r_sock->get(), POLLIN, 0};
EXPECT_THAT(RetryEINTR(poll)(&poll_fd, 1, kPollTimeoutMs),
SyscallSucceedsWithValue(1))
<< "write socks[" << w << "] & read socks[" << r << "]";
char recv_buf[kSendBufSize] = {};
EXPECT_THAT(RetryEINTR(recv)(r_sock->get(), recv_buf, kSendBufSize, 0),
SyscallSucceedsWithValue(kSendBufSize))
<< "write socks[" << w << "] & read socks[" << r << "]";
EXPECT_EQ(0, memcmp(send_buf, recv_buf, kSendBufSize))
<< "write socks[" << w << "] & read socks[" << r << "]";
}
}
}
} // namespace testing
} // namespace gvisor