2019-04-29 21:25:05 +00:00
|
|
|
// Copyright 2018 The gVisor Authors.
|
2018-12-10 22:41:40 +00:00
|
|
|
//
|
|
|
|
// 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 <arpa/inet.h>
|
|
|
|
#include <sys/socket.h>
|
|
|
|
#include <sys/types.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"
|
|
|
|
|
|
|
|
namespace gvisor {
|
|
|
|
namespace testing {
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
|
|
|
struct sockaddr_in_common {
|
|
|
|
sa_family_t sin_family;
|
|
|
|
in_port_t sin_port;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct SendtoTestParam {
|
|
|
|
// Human readable description of test parameter.
|
|
|
|
std::string description;
|
|
|
|
|
|
|
|
// Test is broken in gVisor, skip.
|
|
|
|
bool skip_on_gvisor;
|
|
|
|
|
|
|
|
// Domain for the socket that will do the sending.
|
|
|
|
int send_domain;
|
|
|
|
|
|
|
|
// Address to bind for the socket that will do the sending.
|
|
|
|
struct sockaddr_storage send_addr;
|
|
|
|
socklen_t send_addr_len; // 0 for unbound.
|
|
|
|
|
|
|
|
// Address to connect to for the socket that will do the sending.
|
|
|
|
struct sockaddr_storage connect_addr;
|
|
|
|
socklen_t connect_addr_len; // 0 for no connection.
|
|
|
|
|
|
|
|
// Domain for the socket that will do the receiving.
|
|
|
|
int recv_domain;
|
|
|
|
|
|
|
|
// Address to bind for the socket that will do the receiving.
|
|
|
|
struct sockaddr_storage recv_addr;
|
|
|
|
socklen_t recv_addr_len;
|
|
|
|
|
|
|
|
// Address to send to.
|
|
|
|
struct sockaddr_storage sendto_addr;
|
|
|
|
socklen_t sendto_addr_len;
|
|
|
|
|
|
|
|
// Expected errno for the sendto call.
|
|
|
|
std::vector<int> sendto_errnos; // empty on success.
|
|
|
|
};
|
|
|
|
|
|
|
|
class SendtoTest : public ::testing::TestWithParam<SendtoTestParam> {
|
|
|
|
protected:
|
|
|
|
SendtoTest() {
|
|
|
|
// gUnit uses printf, so so will we.
|
|
|
|
printf("Testing with %s\n", GetParam().description.c_str());
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
TEST_P(SendtoTest, Sendto) {
|
|
|
|
auto param = GetParam();
|
|
|
|
|
|
|
|
SKIP_IF(param.skip_on_gvisor && IsRunningOnGvisor());
|
|
|
|
|
|
|
|
const FileDescriptor s1 =
|
|
|
|
ASSERT_NO_ERRNO_AND_VALUE(Socket(param.send_domain, SOCK_DGRAM, 0));
|
|
|
|
const FileDescriptor s2 =
|
|
|
|
ASSERT_NO_ERRNO_AND_VALUE(Socket(param.recv_domain, SOCK_DGRAM, 0));
|
|
|
|
|
|
|
|
if (param.send_addr_len > 0) {
|
|
|
|
ASSERT_THAT(bind(s1.get(), reinterpret_cast<sockaddr*>(¶m.send_addr),
|
|
|
|
param.send_addr_len),
|
|
|
|
SyscallSucceeds());
|
|
|
|
}
|
|
|
|
|
|
|
|
if (param.connect_addr_len > 0) {
|
|
|
|
ASSERT_THAT(
|
|
|
|
connect(s1.get(), reinterpret_cast<sockaddr*>(¶m.connect_addr),
|
|
|
|
param.connect_addr_len),
|
|
|
|
SyscallSucceeds());
|
|
|
|
}
|
|
|
|
|
|
|
|
ASSERT_THAT(bind(s2.get(), reinterpret_cast<sockaddr*>(¶m.recv_addr),
|
|
|
|
param.recv_addr_len),
|
|
|
|
SyscallSucceeds());
|
|
|
|
|
|
|
|
struct sockaddr_storage real_recv_addr = {};
|
|
|
|
socklen_t real_recv_addr_len = param.recv_addr_len;
|
|
|
|
ASSERT_THAT(
|
|
|
|
getsockname(s2.get(), reinterpret_cast<sockaddr*>(&real_recv_addr),
|
|
|
|
&real_recv_addr_len),
|
|
|
|
SyscallSucceeds());
|
|
|
|
|
|
|
|
ASSERT_EQ(real_recv_addr_len, param.recv_addr_len);
|
|
|
|
|
|
|
|
int recv_port =
|
|
|
|
reinterpret_cast<sockaddr_in_common*>(&real_recv_addr)->sin_port;
|
|
|
|
|
|
|
|
struct sockaddr_storage sendto_addr = param.sendto_addr;
|
|
|
|
reinterpret_cast<sockaddr_in_common*>(&sendto_addr)->sin_port = recv_port;
|
|
|
|
|
|
|
|
char buf[20] = {};
|
|
|
|
if (!param.sendto_errnos.empty()) {
|
|
|
|
ASSERT_THAT(RetryEINTR(sendto)(s1.get(), buf, sizeof(buf), 0,
|
|
|
|
reinterpret_cast<sockaddr*>(&sendto_addr),
|
|
|
|
param.sendto_addr_len),
|
|
|
|
SyscallFailsWithErrno(ElementOf(param.sendto_errnos)));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
ASSERT_THAT(RetryEINTR(sendto)(s1.get(), buf, sizeof(buf), 0,
|
|
|
|
reinterpret_cast<sockaddr*>(&sendto_addr),
|
|
|
|
param.sendto_addr_len),
|
|
|
|
SyscallSucceedsWithValue(sizeof(buf)));
|
|
|
|
|
|
|
|
struct sockaddr_storage got_addr = {};
|
|
|
|
socklen_t got_addr_len = sizeof(sockaddr_storage);
|
|
|
|
ASSERT_THAT(RetryEINTR(recvfrom)(s2.get(), buf, sizeof(buf), 0,
|
|
|
|
reinterpret_cast<sockaddr*>(&got_addr),
|
|
|
|
&got_addr_len),
|
|
|
|
SyscallSucceedsWithValue(sizeof(buf)));
|
|
|
|
|
|
|
|
ASSERT_GT(got_addr_len, sizeof(sockaddr_in_common));
|
|
|
|
int got_port = reinterpret_cast<sockaddr_in_common*>(&got_addr)->sin_port;
|
|
|
|
|
|
|
|
struct sockaddr_storage sender_addr = {};
|
|
|
|
socklen_t sender_addr_len = sizeof(sockaddr_storage);
|
|
|
|
ASSERT_THAT(getsockname(s1.get(), reinterpret_cast<sockaddr*>(&sender_addr),
|
|
|
|
&sender_addr_len),
|
|
|
|
SyscallSucceeds());
|
|
|
|
|
|
|
|
ASSERT_GT(sender_addr_len, sizeof(sockaddr_in_common));
|
|
|
|
int sender_port =
|
|
|
|
reinterpret_cast<sockaddr_in_common*>(&sender_addr)->sin_port;
|
|
|
|
|
|
|
|
EXPECT_EQ(got_port, sender_port);
|
|
|
|
}
|
|
|
|
|
|
|
|
socklen_t Ipv4Addr(sockaddr_storage* addr, int port = 0) {
|
|
|
|
auto addr4 = reinterpret_cast<sockaddr_in*>(addr);
|
|
|
|
addr4->sin_family = AF_INET;
|
|
|
|
addr4->sin_port = port;
|
|
|
|
inet_pton(AF_INET, "127.0.0.1", &addr4->sin_addr.s_addr);
|
|
|
|
return sizeof(struct sockaddr_in);
|
|
|
|
}
|
|
|
|
|
|
|
|
socklen_t Ipv6Addr(sockaddr_storage* addr, int port = 0) {
|
|
|
|
auto addr6 = reinterpret_cast<sockaddr_in6*>(addr);
|
|
|
|
addr6->sin6_family = AF_INET6;
|
|
|
|
addr6->sin6_port = port;
|
|
|
|
inet_pton(AF_INET6, "::1", &addr6->sin6_addr.s6_addr);
|
|
|
|
return sizeof(struct sockaddr_in6);
|
|
|
|
}
|
|
|
|
|
|
|
|
socklen_t Ipv4MappedIpv6Addr(sockaddr_storage* addr, int port = 0) {
|
|
|
|
auto addr6 = reinterpret_cast<sockaddr_in6*>(addr);
|
|
|
|
addr6->sin6_family = AF_INET6;
|
|
|
|
addr6->sin6_port = port;
|
|
|
|
inet_pton(AF_INET6, "::ffff:127.0.0.1", &addr6->sin6_addr.s6_addr);
|
|
|
|
return sizeof(struct sockaddr_in6);
|
|
|
|
}
|
|
|
|
|
2019-04-29 18:32:48 +00:00
|
|
|
INSTANTIATE_TEST_SUITE_P(
|
2018-12-10 22:41:40 +00:00
|
|
|
UdpBindTest, SendtoTest,
|
|
|
|
::testing::Values(
|
|
|
|
[]() {
|
|
|
|
SendtoTestParam param = {};
|
|
|
|
param.description = "IPv4 mapped IPv6 sendto IPv4 mapped IPv6";
|
|
|
|
param.send_domain = AF_INET6;
|
|
|
|
param.send_addr_len = Ipv4MappedIpv6Addr(¶m.send_addr);
|
|
|
|
param.recv_domain = AF_INET6;
|
|
|
|
param.recv_addr_len = Ipv4MappedIpv6Addr(¶m.recv_addr);
|
|
|
|
param.sendto_addr_len = Ipv4MappedIpv6Addr(¶m.sendto_addr);
|
|
|
|
return param;
|
|
|
|
}(),
|
|
|
|
[]() {
|
|
|
|
SendtoTestParam param = {};
|
|
|
|
param.description = "IPv6 sendto IPv6";
|
|
|
|
param.send_domain = AF_INET6;
|
|
|
|
param.send_addr_len = Ipv6Addr(¶m.send_addr);
|
|
|
|
param.recv_domain = AF_INET6;
|
|
|
|
param.recv_addr_len = Ipv6Addr(¶m.recv_addr);
|
|
|
|
param.sendto_addr_len = Ipv6Addr(¶m.sendto_addr);
|
|
|
|
return param;
|
|
|
|
}(),
|
|
|
|
[]() {
|
|
|
|
SendtoTestParam param = {};
|
|
|
|
param.description = "IPv4 sendto IPv4";
|
|
|
|
param.send_domain = AF_INET;
|
|
|
|
param.send_addr_len = Ipv4Addr(¶m.send_addr);
|
|
|
|
param.recv_domain = AF_INET;
|
|
|
|
param.recv_addr_len = Ipv4Addr(¶m.recv_addr);
|
|
|
|
param.sendto_addr_len = Ipv4Addr(¶m.sendto_addr);
|
|
|
|
return param;
|
|
|
|
}(),
|
|
|
|
[]() {
|
|
|
|
SendtoTestParam param = {};
|
|
|
|
param.description = "IPv4 mapped IPv6 sendto IPv4";
|
|
|
|
param.send_domain = AF_INET6;
|
|
|
|
param.send_addr_len = Ipv4MappedIpv6Addr(¶m.send_addr);
|
|
|
|
param.recv_domain = AF_INET;
|
|
|
|
param.recv_addr_len = Ipv4Addr(¶m.recv_addr);
|
|
|
|
param.sendto_addr_len = Ipv4MappedIpv6Addr(¶m.sendto_addr);
|
|
|
|
return param;
|
|
|
|
}(),
|
|
|
|
[]() {
|
|
|
|
SendtoTestParam param = {};
|
|
|
|
param.description = "IPv4 sendto IPv4 mapped IPv6";
|
|
|
|
param.send_domain = AF_INET;
|
|
|
|
param.send_addr_len = Ipv4Addr(¶m.send_addr);
|
|
|
|
param.recv_domain = AF_INET6;
|
|
|
|
param.recv_addr_len = Ipv4MappedIpv6Addr(¶m.recv_addr);
|
|
|
|
param.sendto_addr_len = Ipv4Addr(¶m.sendto_addr);
|
|
|
|
return param;
|
|
|
|
}(),
|
|
|
|
[]() {
|
|
|
|
SendtoTestParam param = {};
|
|
|
|
param.description = "unbound IPv6 sendto IPv4 mapped IPv6";
|
|
|
|
param.send_domain = AF_INET6;
|
|
|
|
param.recv_domain = AF_INET6;
|
|
|
|
param.recv_addr_len = Ipv4MappedIpv6Addr(¶m.recv_addr);
|
|
|
|
param.sendto_addr_len = Ipv4MappedIpv6Addr(¶m.sendto_addr);
|
|
|
|
return param;
|
|
|
|
}(),
|
|
|
|
[]() {
|
|
|
|
SendtoTestParam param = {};
|
|
|
|
param.description = "unbound IPv6 sendto IPv4";
|
|
|
|
param.send_domain = AF_INET6;
|
|
|
|
param.recv_domain = AF_INET;
|
|
|
|
param.recv_addr_len = Ipv4Addr(¶m.recv_addr);
|
|
|
|
param.sendto_addr_len = Ipv4MappedIpv6Addr(¶m.sendto_addr);
|
|
|
|
return param;
|
|
|
|
}(),
|
|
|
|
[]() {
|
|
|
|
SendtoTestParam param = {};
|
|
|
|
param.description = "IPv6 sendto IPv4";
|
|
|
|
param.send_domain = AF_INET6;
|
|
|
|
param.send_addr_len = Ipv6Addr(¶m.send_addr);
|
|
|
|
param.recv_domain = AF_INET;
|
|
|
|
param.recv_addr_len = Ipv4Addr(¶m.recv_addr);
|
|
|
|
param.sendto_addr_len = Ipv4MappedIpv6Addr(¶m.sendto_addr);
|
|
|
|
param.sendto_errnos = {ENETUNREACH};
|
|
|
|
return param;
|
|
|
|
}(),
|
|
|
|
[]() {
|
|
|
|
SendtoTestParam param = {};
|
|
|
|
param.description = "IPv4 mapped IPv6 sendto IPv6";
|
|
|
|
param.send_domain = AF_INET6;
|
|
|
|
param.send_addr_len = Ipv4MappedIpv6Addr(¶m.send_addr);
|
|
|
|
param.recv_domain = AF_INET6;
|
|
|
|
param.recv_addr_len = Ipv6Addr(¶m.recv_addr);
|
|
|
|
param.sendto_addr_len = Ipv6Addr(¶m.sendto_addr);
|
|
|
|
param.sendto_errnos = {EAFNOSUPPORT};
|
|
|
|
// The errno returned changed in Linux commit c8e6ad0829a723.
|
|
|
|
param.sendto_errnos = {EINVAL, EAFNOSUPPORT};
|
|
|
|
return param;
|
|
|
|
}(),
|
|
|
|
[]() {
|
|
|
|
SendtoTestParam param = {};
|
|
|
|
param.description = "connected IPv4 mapped IPv6 sendto IPv6";
|
|
|
|
param.send_domain = AF_INET6;
|
|
|
|
param.connect_addr_len =
|
|
|
|
Ipv4MappedIpv6Addr(¶m.connect_addr, 5000);
|
|
|
|
param.recv_domain = AF_INET6;
|
|
|
|
param.recv_addr_len = Ipv6Addr(¶m.recv_addr);
|
|
|
|
param.sendto_addr_len = Ipv6Addr(¶m.sendto_addr);
|
|
|
|
// The errno returned changed in Linux commit c8e6ad0829a723.
|
|
|
|
param.sendto_errnos = {EINVAL, EAFNOSUPPORT};
|
|
|
|
return param;
|
|
|
|
}(),
|
|
|
|
[]() {
|
|
|
|
SendtoTestParam param = {};
|
|
|
|
param.description = "connected IPv6 sendto IPv4 mapped IPv6";
|
2019-04-29 21:03:04 +00:00
|
|
|
// TODO(igudger): Determine if this inconsistent behavior is worth
|
2018-12-10 22:41:40 +00:00
|
|
|
// implementing.
|
|
|
|
param.skip_on_gvisor = true;
|
|
|
|
param.send_domain = AF_INET6;
|
|
|
|
param.connect_addr_len = Ipv6Addr(¶m.connect_addr, 5000);
|
|
|
|
param.recv_domain = AF_INET6;
|
|
|
|
param.recv_addr_len = Ipv4MappedIpv6Addr(¶m.recv_addr);
|
|
|
|
param.sendto_addr_len = Ipv4MappedIpv6Addr(¶m.sendto_addr);
|
|
|
|
return param;
|
|
|
|
}(),
|
|
|
|
[]() {
|
|
|
|
SendtoTestParam param = {};
|
|
|
|
param.description = "connected IPv6 sendto IPv4";
|
2019-04-29 21:03:04 +00:00
|
|
|
// TODO(igudger): Determine if this inconsistent behavior is worth
|
2018-12-10 22:41:40 +00:00
|
|
|
// implementing.
|
|
|
|
param.skip_on_gvisor = true;
|
|
|
|
param.send_domain = AF_INET6;
|
|
|
|
param.connect_addr_len = Ipv6Addr(¶m.connect_addr, 5000);
|
|
|
|
param.recv_domain = AF_INET;
|
|
|
|
param.recv_addr_len = Ipv4Addr(¶m.recv_addr);
|
|
|
|
param.sendto_addr_len = Ipv4MappedIpv6Addr(¶m.sendto_addr);
|
|
|
|
return param;
|
|
|
|
}()));
|
|
|
|
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
} // namespace testing
|
|
|
|
} // namespace gvisor
|